﻿//
// (C) Copyright 2009 Irantha Suwandarathna (iranthas@hotmail.com)
// All rights reserved.
//

/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using EffiProz.Core.Errors;
using EffiProz.Core.Lib;
using EffiProz.Core.DataTypes;
using EffiProz.Core.Rights;
using EffiProz.Core.Store;
using EffiProz.Core.Indexes;

namespace EffiProz.Core
{
    public class ParserDDL : ParserRoutine
    {
        public static int[] schemaCommands = new int[] {
        Tokens.CREATE, Tokens.GRANT
    };
        public static short[] endStatementTokens = new short[] {
        Tokens.CREATE, Tokens.GRANT, Tokens.ALTER, Tokens.DROP
    };
        public static short[] endStatementTokensSchema = new short[] {
        Tokens.CREATE, Tokens.GRANT,
    };

        public ParserDDL(Session session, Scanner scanner)
            : base(session, scanner)
        {

        }

        public override void reset(String sql)
        {
            base.reset(sql);
        }

        public StatementSchema compileCreate()
        {

            int tableType = TableBase.MEMORY_TABLE;
            bool isTable = false;

            read();

            switch (token.tokenType)
            {

                case Tokens.GLOBAL:
                    read();
                    readThis(Tokens.TEMPORARY);
                    readIfThis(Tokens.MEMORY);
                    readThis(Tokens.TABLE);

                    isTable = true;
                    tableType = TableBase.TEMP_TABLE;
                    break;

                case Tokens.TEMP:
                    read();
                    readThis(Tokens.TABLE);

                    isTable = true;
                    tableType = TableBase.TEMP_TABLE;
                    break;

                case Tokens.TEMPORARY:
                    read();
                    readThis(Tokens.TABLE);

                    isTable = true;
                    tableType = TableBase.TEMP_TABLE;
                    break;

                case Tokens.MEMORY:
                    read();
                    readThis(Tokens.TABLE);

                    isTable = true;
                    break;

                case Tokens.CACHED:
                    read();
                    readThis(Tokens.TABLE);

                    isTable = true;
                    tableType = TableBase.CACHED_TABLE;
                    break;

                case Tokens.TEXT:
                    read();
                    readThis(Tokens.TABLE);

                    isTable = true;
                    tableType = TableBase.TEXT_TABLE;
                    break;

                case Tokens.TABLE:
                    read();

                    isTable = true;
                    tableType = database.schemaManager.getDefaultTableType();
                    break;             
            }

            if (isTable)
            {
                return compileCreateTable(tableType);
            }

            switch (token.tokenType)
            {

                // other objects
                case Tokens.ALIAS:
                    return compileCreateAlias();

                case Tokens.SEQUENCE:
                    return compileCreateSequence();

                case Tokens.SCHEMA:
                    return compileCreateSchema();

                case Tokens.TRIGGER:
                    return compileCreateTrigger();

                case Tokens.USER:
                    return compileCreateUser();

                case Tokens.ROLE:
                    return compileCreateRole();

                case Tokens.VIEW:
                    return compileCreateView();

                case Tokens.DOMAIN:
                    return compileCreateDomain();

                case Tokens.TYPE:
                    return compileCreateType();

                case Tokens.CHARACTER:
                    return compileCreateCharacterSet();

                // index
                case Tokens.UNIQUE:
                    read();
                    checkIsThis(Tokens.INDEX);

                    return compileCreateIndex(true);

                case Tokens.INDEX:
                    return compileCreateIndex(false);

                case Tokens.FUNCTION:
                case Tokens.PROCEDURE:
                    return compileCreateProcedureOrFunction();

                default:
                    {
                        throw unexpectedToken();
                    }
            }
        }

        public void processAlter()
        {

            session.setScripting(true);
            readThis(Tokens.ALTER);

            switch (token.tokenType)
            {

                case Tokens.TABLE:
                    {
                        read();
                        processAlterTable();

                        break;
                    }
                case Tokens.DOMAIN:
                    {
                        read();
                        processAlterDomain();

                        break;
                    }
                default:
                    {
                        throw unexpectedToken();
                    }
            }
        }

        public Statement compileAlter()
        {

            read();

            switch (token.tokenType)
            {

                case Tokens.INDEX:
                    {
                        read();

                        QNameManager.QName name = readNewSchemaObjectName(SchemaObjectTypes.INDEX,
                            true);

                        readThis(Tokens.RENAME);
                        readThis(Tokens.TO);

                        return compileRenameObject(name, SchemaObjectTypes.INDEX);
                    }
                case Tokens.SCHEMA:
                    {
                        read();

                        QNameManager.QName name = readSchemaName();

                        readThis(Tokens.RENAME);
                        readThis(Tokens.TO);

                        return compileRenameObject(name, SchemaObjectTypes.SCHEMA);
                    }
                case Tokens.CATALOG:
                    {
                        read();
                        checkIsSimpleName();

                        String name = token.tokenString;

                        checkValidCatalogName(name);
                        read();
                        readThis(Tokens.RENAME);
                        readThis(Tokens.TO);

                        return compileRenameObject(database.getCatalogName(),
                                                   SchemaObjectTypes.CATALOG);
                    }
                case Tokens.SEQUENCE:
                    {
                        read();

                        return compileAlterSequence();
                    }
                case Tokens.TABLE:
                    {
                        read();

                        return compileAlterTable();
                    }
                case Tokens.USER:
                    {
                        read();

                        return compileAlterUser();
                    }
                case Tokens.DOMAIN:
                    {
                        read();

                        return compileAlterDomain();
                    }
                default:
                    {
                        throw unexpectedToken();
                    }
            }
        }

        /*
            CompiledStatementInterface compileAlter() {

                CompiledStatementInterface cs = null;
                read();
                String sql = getStatement(getParsePosition(), endStatementTokensAlter);

                cs = new CompiledStatementSchema(sql, StatementCodes.ALTER_TYPE, null);

                return cs;
            }
        */
        public Statement compileDrop()
        {

            int objectTokenType;
            int objectType;
            int statementType;
            bool canCascade = false;
            bool cascade = false;
            bool useIfExists = false;
            bool ifExists = false;

            read();

            objectTokenType = this.token.tokenType;

            switch (objectTokenType)
            {

                case Tokens.INDEX:
                    {
                        read();

                        statementType = StatementTypes.DROP_INDEX;
                        objectType = SchemaObjectTypes.INDEX;
                        useIfExists = true;

                        break;
                    }
                case Tokens.ASSERTION:
                    {
                        read();

                        statementType = StatementTypes.DROP_ASSERTION;
                        objectType = SchemaObjectTypes.ASSERTION;
                        canCascade = true;

                        break;
                    }
                case Tokens.SPECIFIC:
                    {
                        read();

                        switch (token.tokenType)
                        {

                            case Tokens.ROUTINE:
                            case Tokens.PROCEDURE:
                            case Tokens.FUNCTION:
                                read();
                                break;

                            default:
                                throw unexpectedToken();
                        }

                        statementType = StatementTypes.DROP_ROUTINE;
                        objectType = SchemaObjectTypes.SPECIFIC_ROUTINE;
                        canCascade = true;

                        break;
                    }
                case Tokens.PROCEDURE:
                    {
                        read();

                        statementType = StatementTypes.DROP_ROUTINE;
                        objectType = SchemaObjectTypes.PROCEDURE;
                        canCascade = true;

                        break;
                    }
                case Tokens.FUNCTION:
                    {
                        read();

                        statementType = StatementTypes.DROP_ROUTINE;
                        objectType = SchemaObjectTypes.FUNCTION;
                        canCascade = true;

                        break;
                    }
                case Tokens.SCHEMA:
                    {
                        read();

                        statementType = StatementTypes.DROP_SCHEMA;
                        objectType = SchemaObjectTypes.SCHEMA;
                        useIfExists = true;
                        canCascade = true;

                        break;
                    }
                case Tokens.SEQUENCE:
                    {
                        read();

                        statementType = StatementTypes.DROP_SEQUENCE;
                        objectType = SchemaObjectTypes.SEQUENCE;
                        canCascade = true;
                        useIfExists = true;

                        break;
                    }
                case Tokens.TRIGGER:
                    {
                        read();

                        statementType = StatementTypes.DROP_TRIGGER;
                        objectType = SchemaObjectTypes.TRIGGER;
                        canCascade = false;

                        break;
                    }
                case Tokens.USER:
                    {
                        read();

                        statementType = StatementTypes.DROP_USER;
                        objectType = SchemaObjectTypes.GRANTEE;
                        canCascade = true;

                        break;
                    }
                case Tokens.ROLE:
                    {
                        read();

                        statementType = StatementTypes.DROP_ROLE;
                        objectType = SchemaObjectTypes.GRANTEE;
                        canCascade = true;

                        break;
                    }
                case Tokens.DOMAIN:
                    read();

                    statementType = StatementTypes.DROP_DOMAIN;
                    objectType = SchemaObjectTypes.DOMAIN;
                    canCascade = true;
                    break;

                case Tokens.TYPE:
                    read();

                    statementType = StatementTypes.DROP_TYPE;
                    objectType = SchemaObjectTypes.TYPE;
                    canCascade = true;
                    break;

                case Tokens.CHARACTER:
                    read();
                    readThis(Tokens.SET);

                    statementType = StatementTypes.DROP_CHARACTER_SET;
                    objectType = SchemaObjectTypes.CHARSET;
                    canCascade = false;
                    break;

                case Tokens.VIEW:
                    read();

                    statementType = StatementTypes.DROP_VIEW;
                    objectType = SchemaObjectTypes.VIEW;
                    canCascade = true;
                    useIfExists = true;
                    break;

                case Tokens.TABLE:
                    read();

                    statementType = StatementTypes.DROP_TABLE;
                    objectType = SchemaObjectTypes.TABLE;
                    canCascade = true;
                    useIfExists = true;
                    break;

                default:
                    throw unexpectedToken();
            }

            if (useIfExists && token.tokenType == Tokens.IF)
            {
                int position = getPosition();

                read();

                if (token.tokenType == Tokens.EXISTS)
                {
                    read();

                    ifExists = true;
                }
                else
                {
                    rewind(position);
                }
            }

            checkIsIdentifier();

            QNameManager.QName name = null;

            switch (objectTokenType)
            {

                case Tokens.USER:
                    {
                        checkIsSimpleName();
                        checkDatabaseUpdateAuthorisation();

                        Grantee grantee =
                            database.getUserManager().get(token.tokenString);

                        name = grantee.getName();

                        read();

                        break;
                    }
                case Tokens.ROLE:
                    {
                        checkIsSimpleName();
                        checkDatabaseUpdateAuthorisation();

                        Grantee role =
                            database.getGranteeManager().getRole(token.tokenString);

                        name = role.getName();

                        read();

                        break;
                    }
                case Tokens.SCHEMA:
                    {
                        name = readNewSchemaName();

                        break;
                    }
                default:
                    name = readNewSchemaObjectName(objectType, false);
                    break;
            }

            if (!ifExists && useIfExists && token.tokenType == Tokens.IF)
            {
                read();
                readThis(Tokens.EXISTS);

                ifExists = true;
            }

            if (canCascade)
            {
                if (token.tokenType == Tokens.CASCADE)
                {
                    cascade = true;

                    read();
                }
                else if (token.tokenType == Tokens.RESTRICT)
                {
                    read();
                }
            }

            Object[] args = new Object[] {
            name, objectType, cascade,
            ifExists
        };
            String sql = getLastPart();
            Statement cs = new StatementSchema(sql, statementType, args, null,
                                               null);

            switch (objectTokenType)
            {

                case Tokens.INDEX:
                case Tokens.SCHEMA:
                case Tokens.TYPE:
                case Tokens.TABLE:
                case Tokens.CHARACTER:
                    break;
            }

            /** @todo - lock tables and schemas to drop */
            return cs;
        }

        private void processAlterTable()
        {

            String tableName = token.tokenString;
            QNameManager.QName schema = session.getSchemaQName(token.namePrefix);

            checkSchemaUpdateAuthorisation(schema);

            Table t = database.schemaManager.getUserTable(session, tableName,
                schema.name);

            if (t.isView())
            {
                throw Error.error(ErrorCode.X_42501, tableName);
            }

            read();

            switch (token.tokenType)
            {

                case Tokens.RENAME:
                    {
                        read();
                        readThis(Tokens.TO);
                        processAlterTableRename(t);

                        return;
                    }
                case Tokens.ADD:
                    {
                        read();

                        QNameManager.QName cname = null;

                        if (token.tokenType == Tokens.CONSTRAINT)
                        {
                            read();

                            cname = readNewDependentSchemaObjectName(t.getName(),
                                    SchemaObjectTypes.CONSTRAINT);

                            database.schemaManager.checkSchemaObjectNotExists(cname);
                        }

                        switch (token.tokenType)
                        {

                            case Tokens.FOREIGN:
                                read();
                                readThis(Tokens.KEY);
                                processAlterTableAddForeignKeyConstraint(t, cname);

                                return;

                            case Tokens.UNIQUE:
                                read();
                                processAlterTableAddUniqueConstraint(t, cname);

                                return;

                            case Tokens.CHECK:
                                read();
                                processAlterTableAddCheckConstraint(t, cname);

                                return;

                            case Tokens.PRIMARY:
                                read();
                                readThis(Tokens.KEY);
                                processAlterTableAddPrimaryKey(t, cname);

                                return;

                            case Tokens.COLUMN:
                                if (cname != null)
                                {
                                    throw unexpectedToken();
                                }

                                read();
                                checkIsSimpleName();
                                processAlterTableAddColumn(t);

                                return;

                            default:
                                if (cname != null)
                                {
                                    throw unexpectedToken();
                                }

                                checkIsSimpleName();
                                processAlterTableAddColumn(t);

                                return;
                        }
                    }
                case Tokens.DROP:
                    {
                        read();

                        switch (token.tokenType)
                        {

                            case Tokens.PRIMARY:
                                {
                                    bool cascade = false;

                                    read();
                                    readThis(Tokens.KEY);

                                    if (token.tokenType == Tokens.CASCADE)
                                    {
                                        read();

                                        cascade = true;
                                    }

                                    if (t.hasPrimaryKey())
                                    {
                                        processAlterTableDropConstraint(
                                            t, t.getPrimaryConstraint().getName().name,
                                            cascade);
                                    }
                                    else
                                    {
                                        throw Error.error(ErrorCode.X_42501);
                                    }

                                    return;
                                }
                            case Tokens.CONSTRAINT:
                                {
                                    bool cascade = false;

                                    read();

                                    SchemaObject obj = readSchemaObjectName(t.getName(),
                                        SchemaObjectTypes.CONSTRAINT);

                                    if (token.tokenType == Tokens.RESTRICT)
                                    {
                                        read();
                                    }
                                    else if (token.tokenType == Tokens.CASCADE)
                                    {
                                        read();

                                        cascade = true;
                                    }

                                    processAlterTableDropConstraint(
                                        t, obj.getName().name, cascade);

                                    //                        read();
                                    return;
                                }
                            case Tokens.COLUMN:
                                read();
                                goto default;
                            // fall through
                            default:
                                {
                                    checkIsSimpleName();

                                    String name = token.tokenString;
                                    bool cascade = false;

                                    read();

                                    if (token.tokenType == Tokens.RESTRICT)
                                    {
                                        read();
                                    }
                                    else if (token.tokenType == Tokens.CASCADE)
                                    {
                                        read();

                                        cascade = true;
                                    }

                                    processAlterTableDropColumn(t, name, cascade);

                                    return;
                                }
                        }
                    }
                case Tokens.ALTER:
                    {
                        read();

                        if (token.tokenType == Tokens.COLUMN)
                        {
                            read();
                        }

                        int columnIndex = t.getColumnIndex(token.tokenString);
                        ColumnSchema column = t.getColumn(columnIndex);

                        read();
                        processAlterColumn(t, column, columnIndex);

                        return;
                    }
                default:
                    {
                        throw unexpectedToken();
                    }
            }
        }

        Statement compileAlterTable()
        {

            String tableName = token.tokenString;
            QNameManager.QName schema = session.getSchemaQName(token.namePrefix);
            Table t = database.schemaManager.getUserTable(session, tableName,
                schema.name);

            if (t.isView())
            {
                throw Error.error(ErrorCode.X_42501, tableName);
            }

            read();

            switch (token.tokenType)
            {

                case Tokens.RENAME:
                    {
                        read();
                        readThis(Tokens.TO);

                        return compileRenameObject(t.getName(), SchemaObjectTypes.TABLE);
                    }
                case Tokens.ADD:
                    {
                        read();

                        QNameManager.QName cname = null;

                        if (token.tokenType == Tokens.CONSTRAINT)
                        {
                            read();

                            cname = readNewDependentSchemaObjectName(t.getName(),
                                    SchemaObjectTypes.CONSTRAINT);
                        }

                        switch (token.tokenType)
                        {

                            case Tokens.FOREIGN:
                                read();
                                readThis(Tokens.KEY);

                                return compileAlterTableAddForeignKeyConstraint(t,
                                        cname);

                            case Tokens.UNIQUE:
                                read();

                                return compileAlterTableAddUniqueConstraint(t, cname);

                            case Tokens.CHECK:
                                read();

                                return compileAlterTableAddCheckConstraint(t, cname);

                            case Tokens.PRIMARY:
                                read();
                                readThis(Tokens.KEY);

                                return compileAlterTableAddPrimaryKey(t, cname);

                            case Tokens.COLUMN:
                                if (cname != null)
                                {
                                    throw unexpectedToken();
                                }

                                read();
                                checkIsSimpleName();

                                return compileAlterTableAddColumn(t);

                            default:
                                if (cname != null)
                                {
                                    throw unexpectedToken();
                                }

                                checkIsSimpleName();

                                return compileAlterTableAddColumn(t);
                        }
                    }
                case Tokens.DROP:
                    {
                        read();

                        switch (token.tokenType)
                        {

                            case Tokens.PRIMARY:
                                {
                                    //bool cascade = false;

                                    read();
                                    readThis(Tokens.KEY);

                                    return compileAlterTableDropPrimaryKey(t);
                                }
                            case Tokens.CONSTRAINT:
                                {
                                    read();

                                    return compileAlterTableDropConstraint(t);
                                }
                            case Tokens.COLUMN:
                                read();
                                goto default;
                            // fall through
                            default:
                                {
                                    checkIsSimpleName();

                                    String name = token.tokenString;
                                    bool cascade = false;

                                    read();

                                    if (token.tokenType == Tokens.RESTRICT)
                                    {
                                        read();
                                    }
                                    else if (token.tokenType == Tokens.CASCADE)
                                    {
                                        read();

                                        cascade = true;
                                    }

                                    return compileAlterTableDropColumn(t, name, cascade);
                                }
                        }
                    }
                case Tokens.ALTER:
                    {
                        read();

                        if (token.tokenType == Tokens.COLUMN)
                        {
                            read();
                        }

                        int columnIndex = t.getColumnIndex(token.tokenString);
                        ColumnSchema column = t.getColumn(columnIndex);

                        read();

                        return compileAlterColumn(t, column, columnIndex);
                    }
                default:
                    {
                        throw unexpectedToken();
                    }
            }
        }

        private Statement compileAlterTableDropConstraint(Table t)
        {

            bool cascade = false;
            SchemaObject obj = readSchemaObjectName(t.getSchemaName(),
                SchemaObjectTypes.CONSTRAINT);

            if (token.tokenType == Tokens.RESTRICT)
            {
                read();
            }
            else if (token.tokenType == Tokens.CASCADE)
            {
                read();

                cascade = true;
            }

            Object[] args = new Object[] {
            obj.getName(), (SchemaObjectTypes.CONSTRAINT),
            (cascade), false
        };
            String sql = getLastPart();
            Statement cs = new StatementSchema(sql,
                                               StatementTypes.DROP_CONSTRAINT,
                                               args, null, null);

            cs.writeTableNames = getReferenceArray(t.getName(), cascade);

            return cs;
        }

        private Statement compileAlterTableDropPrimaryKey(Table t)
        {

            bool cascade = false;

            if (token.tokenType == Tokens.RESTRICT)
            {
                read();
            }
            else if (token.tokenType == Tokens.CASCADE)
            {
                read();

                cascade = true;
            }

            if (!t.hasPrimaryKey())
            {
                throw Error.error(ErrorCode.X_42501);
            }

            SchemaObject obj = t.getPrimaryConstraint();
            Object[] args = new Object[] {
            obj.getName(),  (SchemaObjectTypes.CONSTRAINT),
             (cascade), (false)
        };
            String sql = getLastPart();
            Statement cs = new StatementSchema(sql,
                                               StatementTypes.DROP_CONSTRAINT,
                                               args, null, null);

            cs.writeTableNames = getReferenceArray(t.getName(), cascade);

            return cs;
        }

        QNameManager.QName[] getReferenceArray(QNameManager.QName objectName, bool cascade)
        {

            if (cascade)
            {
                OrderedHashSet names = new OrderedHashSet();

                database.schemaManager.getCascadingReferences(objectName, names);

                Iterator it = names.iterator();

                while (it.hasNext())
                {
                    QNameManager.QName name = (QNameManager.QName)it.next();

                    if (name.type != SchemaObjectTypes.TABLE)
                    {
                        it.remove();
                    }
                }

                names.add(objectName);

                QNameManager.QName[] array = new QNameManager.QName[names.size()];

                names.toArray(array);

                return array;
            }
            else
            {
                return new QNameManager.QName[] { objectName };
            }
        }

        StatementSchema compileCreateTable(int type)
        {

            QNameManager.QName name = readNewSchemaObjectName(SchemaObjectTypes.TABLE, false);
            EfzArrayList tempConstraints = new EfzArrayList();

            name.setSchemaIfNull(session.getCurrentSchemaHsqlName());

            Table table = TableUtil.newTable(database, type, name);

            if (token.tokenType == Tokens.AS)
            {
                return readTableAsSubqueryDefinition(table);
            }

            int position = getPosition();

            readThis(Tokens.OPENBRACKET);

            {
                Constraint c = new Constraint(null, null,
                                              SchemaObjectConstraintTypes.TEMP);

                tempConstraints.add(c);
            }

            bool start = true;
            bool startPart = true;
            bool end = false;

            while (!end)
            {
                switch (token.tokenType)
                {

                    case Tokens.LIKE:
                        {
                            ColumnSchema[] likeColumns = readLikeTable(table);

                            for (int i = 0; i < likeColumns.Length; i++)
                            {
                                table.addColumn(likeColumns[i]);
                            }

                            start = false;
                            startPart = false;

                            break;
                        }
                    case Tokens.CONSTRAINT:
                    case Tokens.PRIMARY:
                    case Tokens.FOREIGN:
                    case Tokens.UNIQUE:
                    case Tokens.CHECK:
                        if (!startPart)
                        {
                            throw unexpectedToken();
                        }

                        readConstraint(table, tempConstraints);

                        start = false;
                        startPart = false;
                        break;

                    case Tokens.COMMA:
                        if (startPart)
                        {
                            throw unexpectedToken();
                        }

                        read();

                        startPart = true;
                        break;

                    case Tokens.CLOSEBRACKET:
                        read();

                        end = true;
                        break;

                    default:
                        if (!startPart)
                        {
                            throw unexpectedToken();
                        }

                        checkIsSchemaObjectName();

                        QNameManager.QName hsqlName =
                            database.nameManager.newColumnHsqlName(name,
                                token.tokenString, isDelimitedIdentifier());
                        hsqlName.originalName = token.tokenStringOrginal;

                        read();

                        ColumnSchema newcolumn = readColumnDefinitionOrNull(table,
                            hsqlName, tempConstraints);

                        if (newcolumn == null)
                        {
                            if (start)
                            {
                                rewind(position);

                                return readTableAsSubqueryDefinition(table);
                            }
                            else
                            {
                                throw Error.error(ErrorCode.X_42000);
                            }
                        }

                        table.addColumn(newcolumn);

                        start = false;
                        startPart = false;
                        break;
                }
            }

            if (token.tokenType == Tokens.ON)
            {
                if (!table.isTemp())
                {
                    throw unexpectedToken();
                }

                read();
                readThis(Tokens.COMMIT);

                if (token.tokenType == Tokens.DELETE) { }
                else if (token.tokenType == Tokens.PRESERVE)
                {
                    table.persistenceScope = TableBase.SCOPE_SESSION;
                }

                read();
                readThis(Tokens.ROWS);
            }

            Object[] args = new Object[] {
            table, tempConstraints, null
        };
            String sql = getLastPart();

            return new StatementSchema(sql, StatementTypes.CREATE_TABLE, args,
                                       null, null);
        }

        private ColumnSchema[] readLikeTable(Table table)
        {

            read();

            bool generated = false;
            bool identity = false;
            bool defaults = false;
            Table likeTable = readTableName();
            OrderedIntHashSet set = new OrderedIntHashSet();

            while (true)
            {
                bool including = token.tokenType == Tokens.INCLUDING;

                if (!including && token.tokenType != Tokens.EXCLUDING)
                {
                    break;
                }

                read();

                switch (token.tokenType)
                {

                    case Tokens.GENERATED:
                        if (!set.add(token.tokenType))
                        {
                            throw unexpectedToken();
                        }

                        generated = including;
                        break;

                    case Tokens.IDENTITY:
                        if (!set.add(token.tokenType))
                        {
                            throw unexpectedToken();
                        }

                        identity = including;
                        break;

                    case Tokens.DEFAULTS:
                        if (!set.add(token.tokenType))
                        {
                            throw unexpectedToken();
                        }

                        defaults = including;
                        break;

                    default:
                        throw unexpectedToken();
                }

                read();
            }

            ColumnSchema[] columnList =
                new ColumnSchema[likeTable.getColumnCount()];

            for (int i = 0; i < columnList.Length; i++)
            {
                ColumnSchema column = likeTable.getColumn(i).duplicate();
                QNameManager.QName name =
                    database.nameManager.newColumnSchemaHsqlName(table.getName(),
                        column.getName());

                column.setName(name);

                if (identity)
                {
                    if (column.isIdentity())
                    {
                        column.setIdentity(
                            column.getIdentitySequence().duplicate());
                    }
                }
                else
                {
                    column.setIdentity(null);
                }

                if (!defaults)
                {
                    column.setDefaultExpression(null);
                }

                if (!generated)
                {
                    column.setGeneratingExpression(null);
                }

                columnList[i] = column;
            }

            return columnList;
        }

        public StatementSchema readTableAsSubqueryDefinition(Table table)
        {

            //QNameManager.QName readName = null;
            bool withData = true;
            QNameManager.QName[] columnNames = null;
            Statement statement = null;

            if (token.tokenType == Tokens.OPENBRACKET)
            {
                columnNames = readColumnNames(table.getName());
            }

            readThis(Tokens.AS);
            readThis(Tokens.OPENBRACKET);

            QueryExpression queryExpression = XreadQueryExpression();

            queryExpression.setAsTopLevel();
            queryExpression.resolve(session);
            readThis(Tokens.CLOSEBRACKET);
            readThis(Tokens.WITH);

            if (token.tokenType == Tokens.NO)
            {
                read();

                withData = false;
            }
            else if (table.getTableType() == TableBase.TEXT_TABLE)
            {
                throw unexpectedTokenRequire(Tokens.T_NO);
            }

            readThis(Tokens.DATA);

            if (token.tokenType == Tokens.ON)
            {
                if (!table.isTemp())
                {
                    throw unexpectedToken();
                }

                read();
                readThis(Tokens.COMMIT);

                if (token.tokenType == Tokens.DELETE) { }
                else if (token.tokenType == Tokens.PRESERVE)
                {
                    table.persistenceScope = TableBase.SCOPE_SESSION;
                }

                read();
                readThis(Tokens.ROWS);
            }

            TableUtil.setColumnsInSchemaTable(
                table, queryExpression.getResultColumnNames(),
                queryExpression.getColumnTypes(),null);

            if (columnNames != null)
            {
                if (columnNames.Length != queryExpression.getColumnCount())
                {
                    throw Error.error(ErrorCode.X_42593);
                }

                for (int i = 0; i < columnNames.Length; i++)
                {
                    table.getColumn(i).getName().rename(columnNames[i]);
                }
            }

            table.createPrimaryKey();

            if (withData)
            {
                statement = new StatementQuery(session, queryExpression,
                                               compileContext);
            }

            Object[] args = new Object[] {
            table, null, statement
        };
            String sql = getLastPart();
            StatementSchema st = new StatementSchema(sql,
                StatementTypes.CREATE_TABLE, args, null, null);

            if (statement != null)
            {
                st.readTableNames = statement.getTableNamesForRead();
            }

            return st;
        }

        /**
         * Adds a list of temp constraints to a new table
         */
        public static Table addTableConstraintDefinitions(Session session, Table table,
                EfzArrayList tempConstraints, EfzArrayList constraintList)
        {

            Constraint c = (Constraint)tempConstraints.get(0);
            String namePart = c.getName() == null ? null
                                                      : c.getName().name;
            QNameManager.QName indexName = session.database.nameManager.newAutoName("IDX",
                namePart, table.getSchemaName(), table.getName(),
                SchemaObjectTypes.INDEX);

            if (c.mainColSet != null)
            {
                c.core.mainCols = table.getColumnIndexes(c.mainColSet);
            }

            table.createPrimaryKey(indexName, c.core.mainCols, true);

            if (c.core.mainCols != null)
            {
                Constraint newconstraint = new Constraint(c.getName(), table,
                    table.getPrimaryIndex(),
                    SchemaObjectConstraintTypes.PRIMARY_KEY);

                table.addConstraint(newconstraint);
                session.database.schemaManager.addSchemaObject(newconstraint);
            }

            for (int i = 1; i < tempConstraints.size(); i++)
            {
                c = (Constraint)tempConstraints.get(i);

                switch (c.constType)
                {

                    case SchemaObjectConstraintTypes.UNIQUE:
                        {
                            c.setColumnsIndexes(table);

                            if (table.getUniqueConstraintForColumns(c.core.mainCols)
                                    != null)
                            {
                                throw Error.error(ErrorCode.X_42522);
                            }

                            // create an autonamed index
                            indexName = session.database.nameManager.newAutoName("IDX",
                                    c.getName().name, table.getSchemaName(),
                                    table.getName(), SchemaObjectTypes.INDEX);

                            Index index = table.createAndAddIndexStructure(indexName,
                                c.core.mainCols, null, null, true, true, false);
                            Constraint newconstraint = new Constraint(c.getName(),
                                table, index, SchemaObjectConstraintTypes.UNIQUE);

                            table.addConstraint(newconstraint);
                            session.database.schemaManager.addSchemaObject(
                                newconstraint);

                            break;
                        }
                    case SchemaObjectConstraintTypes.FOREIGN_KEY:
                        {
                            addForeignKey(session, table, c, constraintList);

                            break;
                        }
                    case SchemaObjectConstraintTypes.CHECK:
                        {
                            c.prepareCheckConstraint(session, table, false);
                            table.addConstraint(c);

                            if (c.isNotNull())
                            {
                                ColumnSchema column =
                                    table.getColumn(c.notNullColumnIndex);

                                column.setNullable(false);
                                table.setColumnTypeVars(c.notNullColumnIndex);
                            }

                            session.database.schemaManager.addSchemaObject(c);

                            break;
                        }
                }
            }

            return table;
        }

        public static void addForeignKey(Session session, Table table, Constraint c,
                                  EfzArrayList constraintList)
        {

            QNameManager.QName mainTableName = c.getMainTableName();

            if (mainTableName == table.getName())
            {
                c.core.mainTable = table;
            }
            else
            {
                Table mainTable =
                    session.database.schemaManager.findUserTable(session,
                        mainTableName.name, mainTableName.schema.name);

                if (mainTable == null)
                {
                    if (constraintList == null)
                    {
                        throw Error.error(ErrorCode.X_42501, mainTableName.name);
                    }

                    constraintList.add(c);

                    return;
                }

                c.core.mainTable = mainTable;
            }

            c.setColumnsIndexes(table);

            Constraint uniqueConstraint =
                c.core.mainTable.getUniqueConstraintForColumns(c.core.mainCols,
                    c.core.refCols);

            if (uniqueConstraint == null)
            {
                throw Error.error(ErrorCode.X_42523);
            }

            Index mainIndex = uniqueConstraint.getMainIndex();
            TableWorks tableWorks = new TableWorks(session, table);

            tableWorks.checkCreateForeignKey(c);

            bool isForward = c.core.mainTable.getSchemaName()
                                != table.getSchemaName();
            int offset = session.database.schemaManager.getTableIndex(table);

            if (offset != -1
                    && offset
                       < session.database.schemaManager.getTableIndex(
                           c.core.mainTable))
            {
                isForward = true;
            }

            QNameManager.QName refIndexName = session.database.nameManager.newAutoName("IDX",
                table.getSchemaName(), table.getName(), SchemaObjectTypes.INDEX);
            Index index = table.createAndAddIndexStructure(refIndexName,
                c.core.refCols, null, null, false, true, isForward);
            QNameManager.QName mainName = session.database.nameManager.newAutoName("REF",
                c.getName().name, table.getSchemaName(), table.getName(),
                SchemaObjectTypes.INDEX);

            c.core.uniqueName = uniqueConstraint.getName();
            c.core.mainName = mainName;
            c.core.mainIndex = mainIndex;
            c.core.refTable = table;
            c.core.refName = c.getName();
            c.core.refIndex = index;
            c.isForward = isForward;

            table.addConstraint(c);
            c.core.mainTable.addConstraint(new Constraint(mainName, c));
            session.database.schemaManager.addSchemaObject(c);
        }

        private Constraint readFKReferences(Table refTable,
                                            QNameManager.QName constraintName,
                                            OrderedHashSet refColSet)
        {

            QNameManager.QName mainTableName;
            OrderedHashSet mainColSet = null;

            readThis(Tokens.REFERENCES);

            QNameManager.QName schema;

            if (token.namePrefix == null)
            {
                schema = refTable.getSchemaName();
            }
            else
            {
                schema =
                    database.schemaManager.getSchemaHsqlName(token.namePrefix);
            }

            if (refTable.getSchemaName() == schema
                    && refTable.getName().name.Equals(token.tokenString))
            {
                mainTableName = refTable.getName();

                read();
            }
            else
            {
                mainTableName = readFKTableName(schema);
            }

            if (token.tokenType == Tokens.OPENBRACKET)
            {
                mainColSet = readColumnNames(false);
            }
            else
            {

                // columns are resolved in the calling method
                if (mainTableName == refTable.getName())
                {

                    // fredt - FK statement is part of CREATE TABLE and is self-referencing
                    // reference must be to same table being created
                }
                else
                {
                    /*
                                    if (!mainTable.hasPrimaryKey()) {
                                        throw Trace.error(Trace.CONSTRAINT_NOT_FOUND,
                                                          Trace.TABLE_HAS_NO_PRIMARY_KEY);

                                    }
                    */
                }
            }

            int matchType = OpTypes.MATCH_SIMPLE;

            if (token.tokenType == Tokens.MATCH)
            {
                read();

                switch (token.tokenType)
                {

                    case Tokens.SIMPLE:
                        read();
                        break;

                    case Tokens.PARTIAL:
                        throw base.unsupportedFeature();
                    case Tokens.FULL:
                        read();

                        matchType = OpTypes.MATCH_FULL;
                        break;

                    default:
                        throw unexpectedToken();
                }
            }

            // -- In a while loop we parse a maximium of two
            // -- "ON" statements following the foreign key
            // -- definition this can be
            // -- ON [UPDATE|DELETE] [NO ACTION|RESTRICT|CASCADE|SET [NULL|DEFAULT]]
            int deleteAction = SchemaObjectReferentialAction.NO_ACTION;
            int updateAction = SchemaObjectReferentialAction.NO_ACTION;
            OrderedIntHashSet set = new OrderedIntHashSet();

            while (token.tokenType == Tokens.ON)
            {
                read();

                if (!set.add(token.tokenType))
                {
                    throw unexpectedToken();
                }

                if (token.tokenType == Tokens.DELETE)
                {
                    read();

                    if (token.tokenType == Tokens.SET)
                    {
                        read();

                        switch (token.tokenType)
                        {

                            case Tokens.DEFAULT:
                                {
                                    read();

                                    deleteAction =
                                        SchemaObjectReferentialAction.SET_DEFAULT;

                                    break;
                                }
                            case Tokens.NULL:
                                read();

                                deleteAction =
                                    SchemaObjectReferentialAction.SET_NULL;
                                break;

                            default:
                                throw unexpectedToken();
                        }
                    }
                    else if (token.tokenType == Tokens.CASCADE)
                    {
                        read();

                        deleteAction = SchemaObjectReferentialAction.CASCADE;
                    }
                    else if (token.tokenType == Tokens.RESTRICT)
                    {
                        read();
                    }
                    else
                    {
                        readThis(Tokens.NO);
                        readThis(Tokens.ACTION);
                    }
                }
                else if (token.tokenType == Tokens.UPDATE)
                {
                    read();

                    if (token.tokenType == Tokens.SET)
                    {
                        read();

                        switch (token.tokenType)
                        {

                            case Tokens.DEFAULT:
                                {
                                    read();

                                    updateAction =
                                        SchemaObjectReferentialAction.SET_DEFAULT;

                                    break;
                                }
                            case Tokens.NULL:
                                read();

                                updateAction =
                                    SchemaObjectReferentialAction.SET_NULL;
                                break;

                            default:
                                throw unexpectedToken();
                        }
                    }
                    else if (token.tokenType == Tokens.CASCADE)
                    {
                        read();

                        updateAction = SchemaObjectReferentialAction.CASCADE;
                    }
                    else if (token.tokenType == Tokens.RESTRICT)
                    {
                        read();
                    }
                    else
                    {
                        readThis(Tokens.NO);
                        readThis(Tokens.ACTION);
                    }
                }
                else
                {
                    throw unexpectedToken();
                }
            }

            if (constraintName == null)
            {
                constraintName = database.nameManager.newAutoName("FK",
                        refTable.getSchemaName(), refTable.getName(),
                        SchemaObjectTypes.CONSTRAINT);
            }

            return new Constraint(constraintName, refTable.getName(), refColSet,
                                  mainTableName, mainColSet,
                                  SchemaObjectConstraintTypes.FOREIGN_KEY,
                                  deleteAction, updateAction, matchType);
        }

        private QNameManager.QName readFKTableName(QNameManager.QName schema)
        {

            QNameManager.QName name;

            checkIsSchemaObjectName();

            Table table = database.schemaManager.findUserTable(session,
                token.tokenString, schema.name);

            if (table == null)
            {
                name = database.nameManager.newHsqlName(schema, token.tokenString,
                        isDelimitedIdentifier(), SchemaObjectTypes.TABLE);
                name.originalName = token.tokenStringOrginal;
            }
            else
            {
                name = table.getName();
            }

            read();

            return name;
        }

        public void processCreateView()
        {

            StatementSchema cs = compileCreateView();
            View view = (View)cs.arguments[0];

            checkSchemaUpdateAuthorisation(view.getSchemaName());
            database.schemaManager.checkSchemaObjectNotExists(view.getName());
            database.schemaManager.addSchemaObject(view);
        }

        public StatementSchema compileCreateView()
        {

            read();

            QNameManager.QName name = readNewSchemaObjectName(SchemaObjectTypes.VIEW, true);

            name.setSchemaIfNull(session.getCurrentSchemaHsqlName());

            QNameManager.QName[] colList = null;

            if (token.tokenType == Tokens.OPENBRACKET)
            {
                colList = readColumnNames(name);
            }

            readThis(Tokens.AS);
            startRecording();

            int position = getPosition();
            QueryExpression queryExpression;

            try
            {
                queryExpression = XreadQueryExpression();
            }
            catch (CoreException )
            {
                queryExpression = XreadJoinedTable();
            }

            Token[] statement = getRecordedStatement();
            String sql = getLastPart(position);
            int check = SchemaObjectViewCheckModes.CHECK_NONE;

            if (token.tokenType == Tokens.WITH)
            {
                read();

                check = SchemaObjectViewCheckModes.CHECK_CASCADE;

                if (readIfThis(Tokens.LOCAL))
                {
                    check = SchemaObjectViewCheckModes.CHECK_LOCAL;
                }
                else
                {
                    readIfThis(Tokens.CASCADED);
                }

                readThis(Tokens.CHECK);
                readThis(Tokens.OPTION);
            }

            View view = new View(session, database, name, colList, sql, check);

            queryExpression.setAsTopLevel();
            queryExpression.setView(view);
            queryExpression.resolve(session);
            view.compile(session, null);
            checkSchemaUpdateAuthorisation(name.schema);
            database.schemaManager.checkSchemaObjectNotExists(name);

            String statementSQL = Token.getSQL(statement);

            view.statement = statementSQL;

            String fullSQL = getLastPart();
            Object[] args = new Object[] { view };
            StatementSchema cs = new StatementSchema(fullSQL,
                StatementTypes.CREATE_VIEW, args, null, null);

            /** @todo - should also lock subquery tables */
            OrderedHashSet set = new OrderedHashSet();

            queryExpression.getBaseTableNames(set);

            QNameManager.QName[] names = new QNameManager.QName[set.size()];

            set.toArray(names);

            cs.readTableNames = names;

            return cs;
        }

        public StatementSchema compileCreateSequence()
        {

            read();

            /*
                    CREATE SEQUENCE <name>
                    [AS {INTEGER | BIGINT}]
                    [START WITH <value>]
                    [INCREMENT BY <value>]
            */
            QNameManager.QName name = readNewSchemaObjectName(SchemaObjectTypes.SEQUENCE, false);
            NumberSequence sequence = new NumberSequence(name, SqlType.SQL_INTEGER);

            readSequenceOptions(sequence, true, false);

            String sql = getLastPart();
            Object[] args = new Object[] { sequence };

            return new StatementSchema(sql, StatementTypes.CREATE_SEQUENCE, args,
                                       null, null);
        }

        public StatementSchema compileCreateDomain()
        {

            UserTypeModifier userTypeModifier = null;
            QNameManager.QName name;

            read();

            name = readNewSchemaObjectName(SchemaObjectTypes.DOMAIN, false);

            readIfThis(Tokens.AS);

            SqlType type = readTypeDefinition(false).duplicate();
            Expression defaultClause = null;

            if (readIfThis(Tokens.DEFAULT))
            {
                defaultClause = readDefaultClause(type);
            }

            userTypeModifier = new UserTypeModifier(name, SchemaObjectTypes.DOMAIN,
                    type);

            userTypeModifier.setDefaultClause(defaultClause);

            type.userTypeModifier = userTypeModifier;

            EfzArrayList tempConstraints = new EfzArrayList();

            compileContext.currentDomain = type;

            while (true)
            {
                bool end = false;

                switch (token.tokenType)
                {

                    case Tokens.CONSTRAINT:
                    case Tokens.CHECK:
                        readConstraint(type, tempConstraints);
                        break;

                    default:
                        end = true;
                        break;
                }

                if (end)
                {
                    break;
                }
            }

            compileContext.currentDomain = null;

            for (int i = 0; i < tempConstraints.size(); i++)
            {
                Constraint c = (Constraint)tempConstraints.get(i);

                c.prepareCheckConstraint(session, null, false);
                userTypeModifier.addConstraint(c);
            }

            String sql = getLastPart();
            Object[] args = new Object[] { type };

            return new StatementSchema(sql, StatementTypes.CREATE_DOMAIN, args,
                                       null, null);
        }

        public StatementSchema compileCreateType()
        {

            read();

            QNameManager.QName name = readNewSchemaObjectName(SchemaObjectTypes.TYPE, false);

            readThis(Tokens.AS);

            SqlType type = readTypeDefinition(false).duplicate();

            readIfThis(Tokens.FINAL);

            UserTypeModifier userTypeModifier = new UserTypeModifier(name,
                SchemaObjectTypes.TYPE, type);

            type.userTypeModifier = userTypeModifier;

            String sql = getLastPart();
            Object[] args = new Object[] { type };

            return new StatementSchema(sql, StatementTypes.CREATE_TYPE, args,
                                       null, null);
        }

        public StatementSchema compileCreateCharacterSet()
        {

            read();
            readThis(Tokens.SET);

            QNameManager.QName name = readNewSchemaObjectName(SchemaObjectTypes.CHARSET, false);

            readIfThis(Tokens.AS);
            readThis(Tokens.GET);

            String schema = session.getSchemaName(token.namePrefix);
            Charset source =
                (Charset)database.schemaManager.getSchemaObject(token.tokenString,
                    schema, SchemaObjectTypes.CHARSET);

            read();

            if (token.tokenType == Tokens.COLLATION)
            {
                read();
                readThis(Tokens.FROM);
                readThis(Tokens.DEFAULT);
            }

            Charset charset = new Charset(name);

            charset._base = source.getName();

            String sql = getLastPart();
            Object[] args = new Object[] { charset };

            return new StatementSchema(sql, StatementTypes.CREATE_CHARACTER_SET,
                                       args, null, null);
        }

        public StatementSchema compileCreateAlias()
        {

            QNameManager.QName name = null;
            Routine[] routines = null;
            String alias;
            String methodFQN = null;

            if (!session.isProcessingScript())
            {
                throw base.unsupportedFeature();
            }

            read();

            try
            {
                alias = token.tokenString;

                read();
                readThis(Tokens.FOR);

                methodFQN = token.tokenString;

                read();
            }
            catch (CoreException )
            {
                alias = null;
            }

            if (alias != null)
            {
                QNameManager.QName schema =
                    database.schemaManager.getDefaultSchemaHsqlName();

                name = database.nameManager.newHsqlName(schema, alias,
                        SchemaObjectTypes.FUNCTION);

                MethodInfo[] methods = Routine.getMethods(methodFQN);

                routines = Routine.newRoutines(methods);
            }

            String sql = getLastPart();
            Object[] args = new Object[] {
            name, routines
        };

            return new StatementSchema(sql, StatementTypes.CREATE_ALIAS, args,
                                       null, null);
        }

        public StatementSchema compileCreateTrigger()
        {

            Table table;
            bool isForEachRow = false;
            bool isNowait = false;
            bool hasQueueSize = false;
            int queueSize = 0;
            String beforeOrAfter;
            int beforeOrAfterType;
            String operation;
            int operationType;
            String className;
            TriggerDef td;
            QNameManager.QName name;
            QNameManager.QName otherName = null;
            OrderedHashSet columns = null;
            int[] updateColumnIndexes = null;

            read();

            name = readNewSchemaObjectName(SchemaObjectTypes.TRIGGER, true);

            switch (token.tokenType)
            {

                case Tokens.INSTEAD:
                    beforeOrAfter = token.tokenString;
                    beforeOrAfterType = token.tokenType;

                    read();
                    readThis(Tokens.OF);
                    break;

                case Tokens.BEFORE:
                case Tokens.AFTER:
                    beforeOrAfter = token.tokenString;
                    beforeOrAfterType = token.tokenType;

                    read();
                    break;

                default:
                    throw unexpectedToken();
            }

            switch (token.tokenType)
            {

                case Tokens.INSERT:
                case Tokens.DELETE:
                    operation = token.tokenString;
                    operationType = token.tokenType;

                    read();
                    break;

                case Tokens.UPDATE:
                    operation = token.tokenString;
                    operationType = token.tokenType;

                    read();

                    if (token.tokenType == Tokens.OF
                            && beforeOrAfterType != Tokens.INSTEAD)
                    {
                        read();

                        columns = readColumnNameList(null, false,null);
                    }
                    break;

                default:
                    throw unexpectedToken();
            }

            readThis(Tokens.ON);

            table = readTableName();

            if (token.tokenType == Tokens.BEFORE)
            {
                read();
                checkIsSimpleName();

                otherName = readNewSchemaObjectName(SchemaObjectTypes.TRIGGER, true);
            }

            name.setSchemaIfNull(table.getSchemaName());
            checkSchemaUpdateAuthorisation(name.schema);

            if (beforeOrAfterType == Tokens.INSTEAD)
            {
                if (!table.isView()
                        || ((View)table).getCheckOption()
                           == SchemaObjectViewCheckModes.CHECK_CASCADE)
                {
                    throw Error.error(ErrorCode.X_42538, name.schema.name);
                }
            }
            else
            {
                if (table.isView())
                {
                    throw Error.error(ErrorCode.X_42538, name.schema.name);
                }
            }

            if (name.schema != table.getSchemaName())
            {
                throw Error.error(ErrorCode.X_42505, name.schema.name);
            }

            name.parent = table.getName();

            database.schemaManager.checkSchemaObjectNotExists(name);

            if (columns != null)
            {
                updateColumnIndexes = table.getColumnIndexes(columns);

                for (int i = 0; i < updateColumnIndexes.Length; i++)
                {
                    if (updateColumnIndexes[i] == -1)
                    {
                        throw Error.error(ErrorCode.X_42544,
                                          (String)columns.get(i));
                    }
                }
            }

            Expression condition = null;
            String oldTableName = null;
            String newTableName = null;
            String oldRowName = null;
            String newRowName = null;
            Table[] transitions = new Table[4];
            RangeVariable[] rangeVars = new RangeVariable[4];
            EfzArrayList compiledStatements = new EfzArrayList();
            String conditionSQL = null;
            String procedureSQL = null;

            if (token.tokenType == Tokens.REFERENCING)
            {
                read();

                if (token.tokenType != Tokens.OLD
                        && token.tokenType != Tokens.NEW)
                {
                    throw unexpectedToken();
                }

                while (true)
                {
                    if (token.tokenType == Tokens.OLD)
                    {
                        if (operationType == Tokens.INSERT)
                        {
                            throw unexpectedToken();
                        }

                        read();

                        if (token.tokenType == Tokens.TABLE)
                        {
                            if (oldTableName != null
                                    || beforeOrAfterType == Tokens.BEFORE)
                            {
                                throw unexpectedToken();
                            }

                            read();
                            readIfThis(Tokens.AS);
                            checkIsSimpleName();

                            oldTableName = token.tokenString;

                            String n = oldTableName;

                            if (n.Equals(newTableName) || n.Equals(oldRowName)
                                    || n.Equals(newRowName))
                            {
                                throw unexpectedToken();
                            }

                            QNameManager.QName hsqlName = database.nameManager.newHsqlName(
                                table.getSchemaName(), n, isDelimitedIdentifier(),
                                SchemaObjectTypes.TRANSITION);
                            Table transition = new Table(table, hsqlName);
                            RangeVariable range = new RangeVariable(transition,
                                null, null, null, compileContext);

                            transitions[TriggerDef.OLD_TABLE] = transition;
                            rangeVars[TriggerDef.OLD_TABLE] = range;
                        }
                        else if (token.tokenType == Tokens.ROW)
                        {
                            if (oldRowName != null)
                            {
                                throw unexpectedToken();
                            }

                            read();
                            readIfThis(Tokens.AS);
                            checkIsSimpleName();

                            oldRowName = token.tokenString;

                            String n = oldRowName;

                            if (n.Equals(newTableName) || n.Equals(oldTableName)
                                    || n.Equals(newRowName))
                            {
                                throw unexpectedToken();
                            }

                            isForEachRow = true;

                            QNameManager.QName hsqlName = database.nameManager.newHsqlName(
                                table.getSchemaName(), n, isDelimitedIdentifier(),
                                SchemaObjectTypes.TRANSITION);
                            Table transition = new Table(table, hsqlName);
                            RangeVariable range = new RangeVariable(transition,
                                null, null, null, compileContext);

                            transitions[TriggerDef.OLD_ROW] = transition;
                            rangeVars[TriggerDef.OLD_ROW] = range;
                        }
                        else
                        {
                            throw unexpectedToken();
                        }
                    }
                    else if (token.tokenType == Tokens.NEW)
                    {
                        if (operationType == Tokens.DELETE)
                        {
                            throw unexpectedToken();
                        }

                        read();

                        if (token.tokenType == Tokens.TABLE)
                        {
                            if (newTableName != null
                                    || beforeOrAfterType == Tokens.BEFORE)
                            {
                                throw unexpectedToken();
                            }

                            read();
                            readIfThis(Tokens.AS);
                            checkIsSimpleName();

                            newTableName = token.tokenString;

                            String n = newTableName;

                            if (n.Equals(oldTableName) || n.Equals(oldRowName)
                                    || n.Equals(newRowName))
                            {
                                throw unexpectedToken();
                            }

                            QNameManager.QName hsqlName = database.nameManager.newHsqlName(
                                table.getSchemaName(), n, isDelimitedIdentifier(),
                                SchemaObjectTypes.TRANSITION);
                            Table transition = new Table(table, hsqlName);
                            RangeVariable range = new RangeVariable(transition,
                                null, null, null, compileContext);

                            transitions[TriggerDef.NEW_TABLE] = transition;
                            rangeVars[TriggerDef.NEW_TABLE] = range;
                        }
                        else if (token.tokenType == Tokens.ROW)
                        {
                            if (newRowName != null)
                            {
                                throw unexpectedToken();
                            }

                            read();
                            readIfThis(Tokens.AS);
                            checkIsSimpleName();

                            newRowName = token.tokenString;
                            isForEachRow = true;

                            String n = newRowName;

                            if (n.Equals(oldTableName) || n.Equals(newTableName)
                                    || n.Equals(oldRowName))
                            {
                                throw unexpectedToken();
                            }

                            QNameManager.QName hsqlName = database.nameManager.newHsqlName(
                                table.getSchemaName(), n, isDelimitedIdentifier(),
                                SchemaObjectTypes.TRANSITION);
                            Table transition = new Table(table, hsqlName);
                            RangeVariable range = new RangeVariable(transition,
                                null, null, null, compileContext);

                            transitions[TriggerDef.NEW_ROW] = transition;
                            rangeVars[TriggerDef.NEW_ROW] = range;
                        }
                        else
                        {
                            throw unexpectedToken();
                        }
                    }
                    else
                    {
                        break;
                    }

                    read();
                }
            }

            if (isForEachRow && token.tokenType != Tokens.FOR)
            {
                throw unexpectedToken();
            }

            if (token.tokenType == Tokens.FOR)
            {
                read();
                readThis(Tokens.EACH);

                if (token.tokenType == Tokens.ROW)
                {
                    isForEachRow = true;
                }
                else if (token.tokenType == Tokens.STATEMENT)
                {
                    if (isForEachRow || beforeOrAfterType == Tokens.BEFORE)
                    {
                        throw unexpectedToken();
                    }
                }
                else
                {
                    throw unexpectedToken();
                }

                read();
            }

            //
            if (rangeVars[TriggerDef.OLD_TABLE] != null) { }

            if (rangeVars[TriggerDef.NEW_TABLE] != null) { }

            //
            if (Tokens.T_QUEUE.Equals(token.tokenString))
            {
                read();

                queueSize = readInteger();
                hasQueueSize = true;
            }

            if (Tokens.T_NOWAIT.Equals(token.tokenString))
            {
                read();

                isNowait = true;
            }

            if (token.tokenType == Tokens.WHEN
                    && beforeOrAfterType != Tokens.INSTEAD)
            {
                read();
                readThis(Tokens.OPENBRACKET);

                int position = getPosition();

                isCheckOrTriggerCondition = true;
                condition = XreadBooleanValueExpression();
                conditionSQL = getLastPart(position);
                isCheckOrTriggerCondition = false;

                readThis(Tokens.CLOSEBRACKET);

                EfzList unresolved = condition.resolveColumnReferences(rangeVars,
                    null);

                ExpressionColumn.checkColumnsResolved(unresolved);
                condition.resolveTypes(session, null);

                if (condition.getDataType() != SqlType.SQL_BOOLEAN)
                {
                    throw Error.error(ErrorCode.X_42568);
                }
            }

            if (token.tokenType == Tokens.CALL)
            {
                read();
                checkIsSimpleName();
                checkIsDelimitedIdentifier();

                className = token.tokenString;

                read();

                td = new TriggerDef(name, beforeOrAfter, operation, isForEachRow,
                                    table, transitions, rangeVars, condition,
                                    conditionSQL, updateColumnIndexes, className,
                                    isNowait, queueSize);

                String sql = getLastPart();
                Object[] args = new Object[] {
                td, otherName
            };

                return new StatementSchema(sql, StatementTypes.CREATE_TRIGGER,
                                           args, null, table.getName());
            }

            //
            if (hasQueueSize)
            {
                throw unexpectedToken(Tokens.T_QUEUE);
            }

            if (isNowait)
            {
                throw unexpectedToken(Tokens.T_NOWAIT);
            }

            // procedure
            bool isBlock = false;

            if (readIfThis(Tokens.BEGIN))
            {
                readThis(Tokens.ATOMIC);

                isBlock = true;
            }

            int position1 = getPosition();

            while (true)
            {
                StatementDMQL cs = null;

                compileContext.reset();

                switch (token.tokenType)
                {

                    case Tokens.INSERT:
                        if (beforeOrAfterType == Tokens.BEFORE)
                        {
                            throw unexpectedToken();
                        }

                        cs = compileInsertStatement(rangeVars);

                        compiledStatements.add(cs);

                        if (isBlock)
                        {
                            readThis(Tokens.SEMICOLON);
                        }
                        break;

                    case Tokens.UPDATE:
                        if (beforeOrAfterType == Tokens.BEFORE)
                        {
                            throw unexpectedToken();
                        }

                        cs = compileUpdateStatement(rangeVars);

                        compiledStatements.add(cs);

                        if (isBlock)
                        {
                            readThis(Tokens.SEMICOLON);
                        }
                        break;

                    case Tokens.DELETE:
                        if (beforeOrAfterType == Tokens.BEFORE)
                        {
                            throw unexpectedToken();
                        }

                        cs = compileDeleteStatement(rangeVars);

                        compiledStatements.add(cs);

                        if (isBlock)
                        {
                            readThis(Tokens.SEMICOLON);
                        }
                        break;

                    case Tokens.MERGE:
                        if (beforeOrAfterType == Tokens.BEFORE)
                        {
                            throw unexpectedToken();
                        }

                        cs = compileMergeStatement(rangeVars);

                        compiledStatements.add(cs);

                        if (isBlock)
                        {
                            readThis(Tokens.SEMICOLON);
                        }
                        break;

                    case Tokens.SET:
                        if (beforeOrAfterType != Tokens.BEFORE
                                || operationType == Tokens.DELETE)
                        {
                            throw unexpectedToken();
                        }

                        cs = compileTriggerSetStatement(table, rangeVars);

                        compiledStatements.add(cs);

                        if (isBlock)
                        {
                            readThis(Tokens.SEMICOLON);
                        }
                        break;

                    case Tokens.END:
                        break;

                    default:
                        throw unexpectedToken();
                }

                if (!isBlock || token.tokenType == Tokens.END)
                {
                    break;
                }
            }

            procedureSQL = getLastPart(position1);

            if (isBlock)
            {
                readThis(Tokens.END);
            }

            StatementDMQL[] csArray = new StatementDMQL[compiledStatements.size()];

            compiledStatements.toArray(csArray);

            OrderedHashSet references = compileContext.getSchemaObjectNames();

            for (int i = 0; i < csArray.Length; i++)
            {
                Table targetTable = csArray[i].targetTable;
                bool[] check = csArray[i].getInsertOrUpdateColumnCheckList();

                if (check != null)
                {
                    targetTable.getColumnNames(check, references);
                }
            }

            references.remove(table.getName());

            td = new TriggerDefSQL(name, beforeOrAfter, operation, isForEachRow,
                                   table, transitions, rangeVars, condition,
                                   conditionSQL, updateColumnIndexes, csArray,
                                   procedureSQL, references);

            String sql1 = getLastPart();
            Object[] args1 = new Object[] {
            td, otherName
        };

            return new StatementSchema(sql1, StatementTypes.CREATE_TRIGGER, args1,
                                       null, table.getName());
        }

        /**
         * Creates SET Statement for a trigger row from this parse context.
         */
        StatementDMQL compileTriggerSetStatement(Table table,
                RangeVariable[] rangeVars)
        {

            read();

            Expression[] updateExpressions;
            int[] columnMap;
            OrderedHashSet colNames = new OrderedHashSet();
            EfzArrayList exprList = new EfzArrayList();
            RangeVariable[] targetRangeVars = new RangeVariable[]{
            rangeVars[TriggerDef.NEW_ROW] };

            readSetClauseList(targetRangeVars, colNames, exprList);

            columnMap = table.getColumnIndexes(colNames);
            updateExpressions = new Expression[exprList.size()];

            exprList.toArray(updateExpressions);
            resolveUpdateExpressions(table, rangeVars, columnMap,
                                     updateExpressions, RangeVariable.emptyArray);

            StatementDMQL cs = new StatementSet(session, table, rangeVars,
                                                columnMap, updateExpressions,
                                                compileContext);

            return cs;
        }

        /**
         * Responsible for handling the creation of table columns during the process
         * of executing CREATE TABLE or ADD COLUMN etc. statements.
         *
         * @param table this table
         * @param hsqlName column name
         * @param constraintList list of constraints
         * @return a Column object with indicated attributes
         */
        ColumnSchema readColumnDefinitionOrNull(Table table, QNameManager.QName hsqlName,
                EfzArrayList constraintList)
        {

            bool isIdentity = false;
            bool isPKIdentity = false;
            bool generatedAlways = false;
            Expression generateExpr = null;
            bool isNullable = true;
            Expression defaultExpr = null;
            SqlType typeObject;
            NumberSequence sequence = null;

            if (token.tokenType == Tokens.IDENTITY)
            {
                read();

                isIdentity = true;
                isPKIdentity = true;
                typeObject = SqlType.SQL_INTEGER;
                sequence = new NumberSequence(null, 0, 1, typeObject);
            }
            else if (token.tokenType == Tokens.COMMA)
            {
                ;
                return null;
            }
            else
            {
                typeObject = readTypeDefinition(true);
            }

            if (isIdentity) { }
            else if (token.tokenType == Tokens.DEFAULT)
            {
                read();

                defaultExpr = readDefaultClause(typeObject);
            }
            else if (token.tokenType == Tokens.GENERATED && !isIdentity)
            {
                read();

                if (token.tokenType == Tokens.BY)
                {
                    read();
                    readThis(Tokens.DEFAULT);
                }
                else
                {
                    readThis(Tokens.ALWAYS);

                    generatedAlways = true;
                }

                readThis(Tokens.AS);

                if (token.tokenType == Tokens.IDENTITY)
                {
                    read();

                    sequence = new NumberSequence(null, typeObject);

                    sequence.setAlways(generatedAlways);

                    if (token.tokenType == Tokens.OPENBRACKET)
                    {
                        read();
                        readSequenceOptions(sequence, false, false);
                        readThis(Tokens.CLOSEBRACKET);
                    }

                    isIdentity = true;
                }
                else if (token.tokenType == Tokens.OPENBRACKET)
                {
                    if (!generatedAlways)
                    {
                        throw base.unexpectedTokenRequire(Tokens.T_ALWAYS);
                    }

                    read();

                    generateExpr = XreadValueExpression();

                    readThis(Tokens.CLOSEBRACKET);
                }
            }
            else if (token.tokenType == Tokens.IDENTITY && !isIdentity)
            {
                read();

                isIdentity = true;
                isPKIdentity = true;
                sequence = new NumberSequence(null, 0, 1, typeObject);
            }

            ColumnSchema column = new ColumnSchema(hsqlName, typeObject,
                                                   isNullable, false, defaultExpr);

            column.setGeneratingExpression(generateExpr);
            readColumnConstraints(table, column, constraintList);

            if (token.tokenType == Tokens.IDENTITY && !isIdentity)
            {
                read();

                isIdentity = true;
                isPKIdentity = true;
                sequence = new NumberSequence(null, 0, 1, typeObject);
            }

            if (isIdentity)
            {
                column.setIdentity(sequence);
            }

            if (isPKIdentity && !column.isPrimaryKey())
            {
                OrderedHashSet set = new OrderedHashSet();

                set.add(column.getName().name);

                QNameManager.QName constName = database.nameManager.newAutoName("PK",
                    table.getSchemaName(), table.getName(),
                    SchemaObjectTypes.CONSTRAINT);
                Constraint c =
                    new Constraint(constName, set,
                                   SchemaObjectConstraintTypes.PRIMARY_KEY);

                constraintList.set(0, c);
                column.setPrimaryKey(true);
            }

            return column;
        }

        private void readSequenceOptions(NumberSequence sequence,
                                         bool withType, bool isAlter)
        {

            OrderedIntHashSet set = new OrderedIntHashSet();

            while (true)
            {
                bool end = false;

                if (set.contains(token.tokenType))
                {
                    throw unexpectedToken();
                }

                switch (token.tokenType)
                {

                    case Tokens.AS:
                        {
                            if (withType)
                            {
                                read();

                                SqlType type = readTypeDefinition(true);

                                sequence.setDefaults(sequence.name, type);

                                break;
                            }

                            throw unexpectedToken();
                        }
                    case Tokens.START:
                        {
                            set.add(token.tokenType);
                            read();
                            readThis(Tokens.WITH);

                            long value = readBigint();

                            sequence.setStartValueNoCheck(value);

                            break;
                        }
                    case Tokens.RESTART:
                        {
                            if (!isAlter)
                            {
                                end = true;

                                break;
                            }

                            set.add(token.tokenType);
                            read();

                            if (readIfThis(Tokens.WITH))
                            {
                                long value = readBigint();

                                sequence.setCurrentValueNoCheck(value);
                            }
                            else
                            {
                                sequence.setStartValueDefault();
                            }

                            break;
                        }
                    case Tokens.INCREMENT:
                        {
                            set.add(token.tokenType);
                            read();
                            readThis(Tokens.BY);

                            long value = readBigint();

                            sequence.setIncrement(value);

                            break;
                        }
                    case Tokens.NO:
                        read();

                        if (token.tokenType == Tokens.MAXVALUE)
                        {
                            sequence.setDefaultMaxValue();
                        }
                        else if (token.tokenType == Tokens.MINVALUE)
                        {
                            sequence.setDefaultMinValue();
                        }
                        else if (token.tokenType == Tokens.CYCLE)
                        {
                            sequence.setCycle(false);
                        }
                        else
                        {
                            throw unexpectedToken();
                        }

                        set.add(token.tokenType);
                        read();
                        break;

                    case Tokens.MAXVALUE:
                        {
                            set.add(token.tokenType);
                            read();

                            long value = readBigint();

                            sequence.setMaxValueNoCheck(value);

                            break;
                        }
                    case Tokens.MINVALUE:
                        {
                            set.add(token.tokenType);
                            read();

                            long value = readBigint();

                            sequence.setMinValueNoCheck(value);

                            break;
                        }
                    case Tokens.CYCLE:
                        set.add(token.tokenType);
                        read();
                        sequence.setCycle(true);
                        break;

                    default:
                        end = true;
                        break;
                }

                if (end)
                {
                    break;
                }
            }

            sequence.checkValues();
        }

        /**
         * Reads and adds a table constraint definition to the list
         *
         * @param schemaObject table or domain
         * @param constraintList list of constraints
         */
        private void readConstraint(SchemaObject schemaObject,
                                    EfzArrayList constraintList)
        {

            QNameManager.QName constName = null;

            if (token.tokenType == Tokens.CONSTRAINT)
            {
                read();

                constName =
                    readNewDependentSchemaObjectName(schemaObject.getName(),
                                                     SchemaObjectTypes.CONSTRAINT);
            }

            switch (token.tokenType)
            {

                case Tokens.PRIMARY:
                    {
                        if (schemaObject.getName().type != SchemaObjectTypes.TABLE)
                        {
                            throw this.unexpectedTokenRequire(Tokens.T_CHECK);
                        }

                        read();
                        readThis(Tokens.KEY);

                        Constraint mainConst;

                        mainConst = (Constraint)constraintList.get(0);

                        if (mainConst.constType
                                == SchemaObjectConstraintTypes.PRIMARY_KEY)
                        {
                            throw Error.error(ErrorCode.X_42532);
                        }

                        if (constName == null)
                        {
                            constName = database.nameManager.newAutoName("PK",
                                    schemaObject.getSchemaName(),
                                    schemaObject.getName(), SchemaObjectTypes.CONSTRAINT);
                        }

                        OrderedHashSet set = readColumnNames(false);
                        Constraint c =
                            new Constraint(constName, set,
                                           SchemaObjectConstraintTypes.PRIMARY_KEY);

                        constraintList.set(0, c);

                        break;
                    }
                case Tokens.UNIQUE:
                    {
                        if (schemaObject.getName().type != SchemaObjectTypes.TABLE)
                        {
                            throw this.unexpectedTokenRequire(Tokens.T_CHECK);
                        }

                        read();

                        OrderedHashSet set = readColumnNames(false);

                        if (constName == null)
                        {
                            constName = database.nameManager.newAutoName("CT",
                                    schemaObject.getSchemaName(),
                                    schemaObject.getName(), SchemaObjectTypes.CONSTRAINT);
                        }

                        Constraint c =
                            new Constraint(constName, set,
                                           SchemaObjectConstraintTypes.UNIQUE);

                        constraintList.add(c);

                        break;
                    }
                case Tokens.FOREIGN:
                    {
                        if (schemaObject.getName().type != SchemaObjectTypes.TABLE)
                        {
                            throw this.unexpectedTokenRequire(Tokens.T_CHECK);
                        }

                        read();
                        readThis(Tokens.KEY);

                        OrderedHashSet set = readColumnNames(false);
                        Constraint c = readFKReferences((Table)schemaObject,
                                                        constName, set);

                        constraintList.add(c);

                        break;
                    }
                case Tokens.CHECK:
                    {
                        read();

                        if (constName == null)
                        {
                            constName = database.nameManager.newAutoName("CT",
                                    schemaObject.getSchemaName(),
                                    schemaObject.getName(), SchemaObjectTypes.CONSTRAINT);
                        }

                        Constraint c =
                            new Constraint(constName, null,
                                           SchemaObjectConstraintTypes.CHECK);

                        readCheckConstraintCondition(c);
                        constraintList.add(c);

                        break;
                    }
                default:
                    {
                        if (constName != null)
                        {
                            throw Error.error(ErrorCode.X_42581);
                        }
                    }
                    break;
            }
        }

        /**
         * Reads column constraints
         */
        void readColumnConstraints(Table table, ColumnSchema column,
                                   EfzArrayList constraintList)
        {

            bool end = false;

            while (true)
            {
                QNameManager.QName constName = null;

                if (token.tokenType == Tokens.CONSTRAINT)
                {
                    read();

                    constName = readNewDependentSchemaObjectName(table.getName(),
                            SchemaObjectTypes.CONSTRAINT);
                }

                switch (token.tokenType)
                {

                    case Tokens.PRIMARY:
                        {
                            read();
                            readThis(Tokens.KEY);

                            Constraint existingConst =
                                (Constraint)constraintList.get(0);

                            if (existingConst.constType
                                    == SchemaObjectConstraintTypes.PRIMARY_KEY)
                            {
                                throw Error.error(ErrorCode.X_42532);
                            }

                            OrderedHashSet set = new OrderedHashSet();

                            set.add(column.getName().name);

                            if (constName == null)
                            {
                                constName = database.nameManager.newAutoName("PK",
                                        table.getSchemaName(), table.getName(),
                                        SchemaObjectTypes.CONSTRAINT);
                            }

                            Constraint c = new Constraint(
                                constName, set,
                                SchemaObjectConstraintTypes.PRIMARY_KEY);

                            constraintList.set(0, c);
                            column.setPrimaryKey(true);

                            break;
                        }
                    case Tokens.UNIQUE:
                        {
                            read();

                            OrderedHashSet set = new OrderedHashSet();

                            set.add(column.getName().name);

                            if (constName == null)
                            {
                                constName = database.nameManager.newAutoName("CT",
                                        table.getSchemaName(), table.getName(),
                                        SchemaObjectTypes.CONSTRAINT);
                            }

                            Constraint c =
                                new Constraint(constName, set,
                                               SchemaObjectConstraintTypes.UNIQUE);

                            constraintList.add(c);

                            break;
                        }
                    case Tokens.FOREIGN:
                        {
                            read();
                            readThis(Tokens.KEY);
                        }
                        goto case Tokens.REFERENCES;
                    // fall through
                    case Tokens.REFERENCES:
                        {
                            OrderedHashSet set = new OrderedHashSet();

                            set.add(column.getName().name);

                            Constraint c = readFKReferences(table, constName, set);

                            constraintList.add(c);

                            break;
                        }
                    case Tokens.CHECK:
                        {
                            read();

                            if (constName == null)
                            {
                                constName = database.nameManager.newAutoName("CT",
                                        table.getSchemaName(), table.getName(),
                                        SchemaObjectTypes.CONSTRAINT);
                            }

                            Constraint c =
                                new Constraint(constName, null,
                                               SchemaObjectConstraintTypes.CHECK);

                            readCheckConstraintCondition(c);

                            OrderedHashSet set = c.getCheckColumnExpressions();

                            for (int i = 0; i < set.size(); i++)
                            {
                                ExpressionColumn e = (ExpressionColumn)set.get(i);

                                if (column.getName().name.Equals(e.getColumnName()))
                                {
                                    if (e.getSchemaName() != null
                                            && e.getSchemaName()
                                               != table.getSchemaName().name)
                                    {
                                        throw Error.error(ErrorCode.X_42505);
                                    }
                                }
                                else
                                {
                                    throw Error.error(ErrorCode.X_42501);
                                }
                            }

                            constraintList.add(c);

                            break;
                        }
                    case Tokens.NOT:
                        {
                            read();
                            readThis(Tokens.NULL);

                            if (constName == null)
                            {
                                constName = database.nameManager.newAutoName("CT",
                                        table.getSchemaName(), table.getName(),
                                        SchemaObjectTypes.CONSTRAINT);
                            }

                            Constraint c =
                                new Constraint(constName, null,
                                               SchemaObjectConstraintTypes.CHECK);

                            c.check = new ExpressionLogical(column);

                            constraintList.add(c);

                            break;
                        }
                    default:
                        end = true;
                        break;
                }

                if (end)
                {
                    break;
                }
            }
        }

        /**
         * Responsible for handling check constraints section of CREATE TABLE ...
         *
         * @param c check constraint
         */
        void readCheckConstraintCondition(Constraint c)
        {

            readThis(Tokens.OPENBRACKET);
            startRecording();

            isCheckOrTriggerCondition = true;

            Expression condition = XreadBooleanValueExpression();

            isCheckOrTriggerCondition = false;

            Token[] tokens = getRecordedStatement();

            readThis(Tokens.CLOSEBRACKET);

            c.check = condition;
            c.checkStatement = Token.getSQL(tokens);
        }

        /**
         * Process a bracketed column list as used in the declaration of SQL
         * CONSTRAINTS and return an array containing the indexes of the columns
         * within the table.
         *
         * @param table table that contains the columns
         * @param ascOrDesc bool
         * @return array of column indexes
         */
        private int[] readColumnList(Table table, bool ascOrDesc)
        {

            OrderedHashSet set = readColumnNames(ascOrDesc);

            return table.getColumnIndexes(set);
        }

        StatementSchema compileCreateIndex(bool unique)
        {

            Table table;
            QNameManager.QName indexHsqlName;

            read();

            indexHsqlName = readNewSchemaObjectName(SchemaObjectTypes.INDEX, true);

            readThis(Tokens.ON);

            table = readTableName();

            QNameManager.QName tableSchema = table.getSchemaName();

            indexHsqlName.setSchemaIfNull(tableSchema);

            indexHsqlName.parent = table.getName();

            if (indexHsqlName.schema != tableSchema)
            {
                throw Error.error(ErrorCode.X_42505);
            }

            indexHsqlName.schema = table.getSchemaName();

            int[] indexColumns = readColumnList(table, true);
            String sql = getLastPart();
            Object[] args = new Object[] {
            table, indexColumns, indexHsqlName,  (unique)
        };

            return new StatementSchema(sql, StatementTypes.CREATE_INDEX, args,
                                       null, table.getName());
        }

        StatementSchema compileCreateSchema()
        {

            QNameManager.QName schemaName = null;
            String authorisation = null;

            read();

            if (token.tokenType != Tokens.AUTHORIZATION)
            {
                schemaName = readNewSchemaName();
            }

            if (token.tokenType == Tokens.AUTHORIZATION)
            {
                read();
                checkIsSimpleName();

                authorisation = token.tokenString;

                read();

                if (schemaName == null)
                {
                    Grantee owner =
                        database.getGranteeManager().get(authorisation);

                    if (owner == null)
                    {
                        throw Error.error(ErrorCode.X_28501, authorisation);
                    }

                    schemaName =
                        database.nameManager.newHsqlName(owner.getName().name,
                                                         isDelimitedIdentifier(),
                                                         SchemaObjectTypes.SCHEMA);           

                    SqlInvariants.checkSchemaNameNotSystem(token.tokenString);
                }
            }

            if (SqlInvariants.PUBLIC_ROLE_NAME.Equals(authorisation))
            {
                throw Error.error(ErrorCode.X_28502, authorisation);
            }

            Grantee owner1 = authorisation == null ? session.getGrantee()
                                                  : database.getGranteeManager()
                                                      .get(authorisation);

            if (owner1 == null)
            {
                throw Error.error(ErrorCode.X_28501, authorisation);
            }

            if (!session.getGrantee().isSchemaCreator())
            {
                throw Error.error(ErrorCode.X_0L000,
                                  session.getGrantee().getNameString());
            }

            if (database.schemaManager.schemaExists(schemaName.name))
            {
                throw Error.error(ErrorCode.X_42504, schemaName.name);
            }

            String sql = getLastPart();
            Object[] args = new Object[] {
            schemaName, owner1
        };
            EfzArrayList list = new EfzArrayList();
            StatementSchema cs = new StatementSchema(sql,
                StatementTypes.CREATE_SCHEMA, args, null, null);

            cs.setSchemaQName(schemaName);
            list.add(cs);
            getCompiledStatementBody(list);

            StatementSchema[] array = new StatementSchema[list.size()];

            list.toArray(array);

            bool swapped;

            do
            {
                swapped = false;

                for (int i = 0; i < array.Length - 1; i++)
                {
                    if (array[i].order > array[i + 1].order)
                    {
                        StatementSchema temp = array[i + 1];

                        array[i + 1] = array[i];
                        array[i] = temp;
                        swapped = true;
                    }
                }
            } while (swapped);

            return new StatementSchemaDefinition(array);
        }

        public void getCompiledStatementBody(EfzList list)
        {

            int position;
            String sql;
            int statementType;

            for (bool end = false; !end; )
            {
                StatementSchema cs = null;

                position = getPosition();

                switch (token.tokenType)
                {

                    case Tokens.CREATE:
                        read();

                        switch (token.tokenType)
                        {

                            // not in schema definition
                            case Tokens.SCHEMA:
                            case Tokens.USER:
                            case Tokens.UNIQUE:
                                throw unexpectedToken();
                            case Tokens.INDEX:
                                statementType = StatementTypes.CREATE_INDEX;
                                sql = getStatement(position,
                                                   endStatementTokensSchema);
                                cs = new StatementSchema(sql, statementType, null,
                                                         null, null);
                                break;

                            case Tokens.SEQUENCE:
                                cs = compileCreateSequence();
                                cs.sql = getLastPart(position);
                                break;

                            case Tokens.ROLE:
                                cs = compileCreateRole();
                                cs.sql = getLastPart(position);
                                break;

                            case Tokens.DOMAIN:
                                statementType = StatementTypes.CREATE_DOMAIN;
                                sql = getStatement(position,
                                                   endStatementTokensSchema);
                                cs = new StatementSchema(sql, statementType, null,
                                                         null, null);
                                break;

                            case Tokens.TYPE:
                                cs = compileCreateType();
                                cs.sql = getLastPart(position);
                                break;

                            case Tokens.CHARACTER:
                                cs = compileCreateCharacterSet();
                                cs.sql = getLastPart(position);
                                break;

                            // no supported
                            case Tokens.ASSERTION:
                                throw unexpectedToken();
                            case Tokens.TABLE:
                            case Tokens.MEMORY:
                            case Tokens.CACHED:
                            case Tokens.TEMP:
                            case Tokens.GLOBAL:
                            case Tokens.TEMPORARY:
                            case Tokens.TEXT:
                                statementType = StatementTypes.CREATE_TABLE;
                                sql = getStatement(position,
                                                   endStatementTokensSchema);
                                cs = new StatementSchema(sql, statementType, null,
                                                         null, null);
                                break;

                            case Tokens.TRIGGER:
                                statementType = StatementTypes.CREATE_TRIGGER;
                                sql = getStatement(position,
                                                   endStatementTokensSchema);
                                cs = new StatementSchema(sql, statementType, null,
                                                         null, null);
                                break;

                            case Tokens.VIEW:
                                statementType = StatementTypes.CREATE_VIEW;
                                sql = getStatement(position,
                                                   endStatementTokensSchema);
                                cs = new StatementSchema(sql, statementType, null,
                                                         null, null);
                                break;

                            case Tokens.FUNCTION:
                                statementType = StatementTypes.CREATE_ROUTINE;
                                sql = getStatement(position,
                                                   endStatementTokensSchema);
                                cs = new StatementSchema(sql, statementType, null,
                                                         null, null);
                                break;

                            case Tokens.PROCEDURE:
                                statementType = StatementTypes.CREATE_ROUTINE;
                                sql = getStatement(position,
                                                   endStatementTokensSchema);
                                cs = new StatementSchema(sql, statementType, null,
                                                         null, null);
                                break;

                            default:
                                throw unexpectedToken();
                        }
                        break;

                    case Tokens.GRANT:
                        cs = compileGrantOrRevoke();
                        cs.sql = getLastPart(position);
                        break;

                    case Tokens.SEMICOLON:
                        read();

                        end = true;
                        break;

                    case Tokens.X_ENDPARSE:
                        end = true;
                        break;

                    default:
                        throw unexpectedToken();
                }

                if (cs != null)
                {
                    cs.isSchemaDefinition = true;

                    list.add(cs);
                }
            }
        }

        public StatementSchema compileCreateRole()
        {

            read();

            QNameManager.QName name = readNewUserIdentifier();
            String sql = getLastPart();
            Object[] args = new Object[] { name };

            return new StatementSchema(sql, StatementTypes.CREATE_ROLE, args,
                                       null, null);
        }

        public StatementSchema compileCreateUser()
        {

            QNameManager.QName name;
            String password;
            bool admin = false;
            Grantee grantor = session.getGrantee();

            read();

            name = readNewUserIdentifier();

            readThis(Tokens.PASSWORD);

            password = readPassword();

            if (token.tokenType == Tokens.ADMIN)
            {
                read();

                admin = true;
            }

            checkDatabaseUpdateAuthorisation();

            String sql = getLastPart();
            Object[] args = new Object[] {
            name, password, grantor,  (admin)
        };

            return new StatementSchema(sql, StatementTypes.CREATE_USER, args,
                                       null, null);
        }

        public QNameManager.QName readNewUserIdentifier()
        {

            checkIsSimpleName();

            String tokenS = token.tokenString;
            bool isQuoted = isDelimitedIdentifier();

            if (tokenS.Equals("SA", StringComparison.OrdinalIgnoreCase))
            {
                tokenS = "SA";
                isQuoted = false;
            }

            QNameManager.QName name = database.nameManager.newHsqlName(tokenS, isQuoted,
                SchemaObjectTypes.GRANTEE);
            name.originalName = token.tokenStringOrginal;

            read();

            return name;
        }

        public String readPassword()
        {

            String tokenS = token.tokenString;

            read();

            return tokenS;
        }

        public Statement compileRenameObject(QNameManager.QName name, int objectType)
        {

            QNameManager.QName newName = readNewSchemaObjectName(objectType, true);
            String sql = getLastPart();
            Object[] args = new Object[] {
            name, newName
        };

            return new StatementSchema(sql, StatementTypes.RENAME_OBJECT, args,
                                       null, null);
        }

        /**
         * Responsible for handling tail of ALTER TABLE ... RENAME ...
         * @param table table
         */
        public void processAlterTableRename(Table table)
        {

            QNameManager.QName name = readNewSchemaObjectName(SchemaObjectTypes.TABLE, true);

            name.setSchemaIfNull(table.getSchemaName());

            if (table.getSchemaName() != name.schema)
            {
                throw Error.error(ErrorCode.X_42505);
            }

            database.schemaManager.renameSchemaObject(table.getName(), name);
        }

        public void processAlterTableAddUniqueConstraint(Table table, QNameManager.QName name)
        {

            if (name == null)
            {
                name = database.nameManager.newAutoName("CT",
                        table.getSchemaName(), table.getName(),
                        SchemaObjectTypes.CONSTRAINT);
            }

            int[] cols = this.readColumnList(table, false);

            session.commit(false);

            TableWorks tableWorks = new TableWorks(session, table);

            tableWorks.addUniqueConstraint(cols, name);
        }

        public Statement compileAlterTableAddUniqueConstraint(Table table,
                QNameManager.QName name)
        {

            if (name == null)
            {
                name = database.nameManager.newAutoName("CT",
                        table.getSchemaName(), table.getName(),
                        SchemaObjectTypes.CONSTRAINT);
            }

            int[] cols = this.readColumnList(table, false);
            String sql = getLastPart();
            Object[] args = new Object[] {
            cols, name
        };

            return new StatementSchema(sql, StatementTypes.ALTER_TABLE, args,
                                       null, table.getName());
        }

        public void processAlterTableAddForeignKeyConstraint(Table table, QNameManager.QName name)
        {

            if (name == null)
            {
                name = database.nameManager.newAutoName("FK",
                        table.getSchemaName(), table.getName(),
                        SchemaObjectTypes.CONSTRAINT);
            }

            OrderedHashSet set = readColumnNames(false);
            Constraint c = readFKReferences(table, name, set);
            QNameManager.QName mainTableName = c.getMainTableName();

            c.core.mainTable = database.schemaManager.getTable(session,
                    mainTableName.name, mainTableName.schema.name);

            c.setColumnsIndexes(table);
            session.commit(false);

            TableWorks tableWorks = new TableWorks(session, table);

            tableWorks.addForeignKey(c);
        }

        public Statement compileAlterTableAddForeignKeyConstraint(Table table,
                QNameManager.QName name)
        {

            if (name == null)
            {
                name = database.nameManager.newAutoName("FK",
                        table.getSchemaName(), table.getName(),
                        SchemaObjectTypes.CONSTRAINT);
            }

            OrderedHashSet set = readColumnNames(false);
            Constraint c = readFKReferences(table, name, set);
            QNameManager.QName mainTableName = c.getMainTableName();

            c.core.mainTable = database.schemaManager.getTable(session,
                    mainTableName.name, mainTableName.schema.name);

            c.setColumnsIndexes(table);

            String sql = getLastPart();
            Object[] args = new Object[] { c };

            return new StatementSchema(sql, StatementTypes.ALTER_TABLE, args,
                                       c.core.mainTableName, table.getName());
        }

        public void processAlterTableAddCheckConstraint(Table table, QNameManager.QName name)
        {

            Constraint check;

            if (name == null)
            {
                name = database.nameManager.newAutoName("CT",
                        table.getSchemaName(), table.getName(),
                        SchemaObjectTypes.CONSTRAINT);
            }

            check = new Constraint(name, null, SchemaObjectConstraintTypes.CHECK);

            readCheckConstraintCondition(check);
            session.commit(false);

            TableWorks tableWorks = new TableWorks(session, table);

            tableWorks.addCheckConstraint(check);
        }

        public Statement compileAlterTableAddCheckConstraint(Table table, QNameManager.QName name)
        {

            Constraint check;

            if (name == null)
            {
                name = database.nameManager.newAutoName("CT",
                        table.getSchemaName(), table.getName(),
                        SchemaObjectTypes.CONSTRAINT);
            }

            check = new Constraint(name, null, SchemaObjectConstraintTypes.CHECK);

            readCheckConstraintCondition(check);

            String sql = getLastPart();
            Object[] args = new Object[] { check };

            return new StatementSchema(sql, StatementTypes.ALTER_TABLE, args,
                                       null, table.getName());
        }

        public void processAlterTableAddColumn(Table table)
        {

            int colIndex = table.getColumnCount();
            EfzArrayList list = new EfzArrayList();
            Constraint constraint =
                new Constraint(null, null, SchemaObjectConstraintTypes.TEMP);

            list.add(constraint);
            checkIsSchemaObjectName();

            QNameManager.QName hsqlName =
                database.nameManager.newColumnHsqlName(table.getName(),
                    token.tokenString, isDelimitedIdentifier());
            hsqlName.originalName = token.tokenStringOrginal;

            read();

            ColumnSchema column = readColumnDefinitionOrNull(table, hsqlName,
                list);

            if (column == null)
            {
                throw Error.error(ErrorCode.X_42000);
            }

            if (token.tokenType == Tokens.BEFORE)
            {
                read();

                colIndex = table.getColumnIndex(token.tokenString);

                read();
            }

            TableWorks tableWorks = new TableWorks(session, table);

            session.commit(false);
            tableWorks.addColumn(column, colIndex, list);

            return;
        }

        public Statement compileAlterTableAddColumn(Table table)
        {

            int colIndex = table.getColumnCount();
            EfzArrayList list = new EfzArrayList();
            Constraint constraint =
                new Constraint(null, null, SchemaObjectConstraintTypes.TEMP);

            list.add(constraint);
            checkIsSchemaObjectName();

            QNameManager.QName hsqlName =
                database.nameManager.newColumnHsqlName(table.getName(),
                    token.tokenString, isDelimitedIdentifier());
            hsqlName.originalName = token.tokenStringOrginal;

            read();

            ColumnSchema column = readColumnDefinitionOrNull(table, hsqlName,
                list);

            if (column == null)
            {
                throw Error.error(ErrorCode.X_42000);
            }

            if (token.tokenType == Tokens.BEFORE)
            {
                read();

                colIndex = table.getColumnIndex(token.tokenString);

                read();
            }

            String sql = getLastPart();
            Object[] args = new Object[] {
            column,colIndex, list
        };

            return new StatementSchema(sql, StatementTypes.ALTER_TABLE, args,
                                       null, table.getName());
        }

        public void processAlterTableAddPrimaryKey(Table table, QNameManager.QName name)
        {

            if (name == null)
            {
                name = session.database.nameManager.newAutoName("PK",
                        table.getSchemaName(), table.getName(),
                        SchemaObjectTypes.CONSTRAINT);
            }

            int[] cols = readColumnList(table, false);
            Constraint constraint =
                new Constraint(name, null,
                               SchemaObjectConstraintTypes.PRIMARY_KEY);

            constraint.core.mainCols = cols;

            session.commit(false);

            TableWorks tableWorks = new TableWorks(session, table);

            tableWorks.addPrimaryKey(constraint, name);
        }

        public Statement compileAlterTableAddPrimaryKey(Table table, QNameManager.QName name)
        {

            if (name == null)
            {
                name = session.database.nameManager.newAutoName("PK",
                        table.getSchemaName(), table.getName(),
                        SchemaObjectTypes.CONSTRAINT);
            }

            int[] cols = readColumnList(table, false);
            Constraint constraint =
                new Constraint(name, null,
                               SchemaObjectConstraintTypes.PRIMARY_KEY);

            constraint.core.mainCols = cols;

            String sql = getLastPart();
            Object[] args = new Object[] { constraint };

            return new StatementSchema(sql, StatementTypes.ALTER_TABLE, args,
                                       null, table.getName());
        }

        /**
         * Responsible for handling tail of ALTER TABLE ... DROP COLUMN ...
         */
        public void processAlterTableDropColumn(Table table, String colName,
                                         bool cascade)
        {

            int colindex = table.getColumnIndex(colName);

            if (table.getColumnCount() == 1)
            {
                throw Error.error(ErrorCode.X_42591);
            }

            session.commit(false);

            TableWorks tableWorks = new TableWorks(session, table);

            tableWorks.dropColumn(colindex, cascade);
        }

        public Statement compileAlterTableDropColumn(Table table, String colName,
                                              bool cascade)
        {

            QNameManager.QName writeName = null;
            int colindex = table.getColumnIndex(colName);

            if (table.getColumnCount() == 1)
            {
                throw Error.error(ErrorCode.X_42591);
            }

            Object[] args = new Object[] {
            table.getColumn(colindex).getName(),
             (SchemaObjectTypes.CONSTRAINT),  (cascade),
             (false)
        };

            if (!table.isTemp())
            {
                writeName = table.getName();
            }

            return new StatementSchema(null, StatementTypes.DROP_COLUMN, args,
                                       null, writeName);
        }

        /**
         * Responsible for handling tail of ALTER TABLE ... DROP CONSTRAINT ...
         */
        public void processAlterTableDropConstraint(Table table, String name,
                                             bool cascade)
        {

            session.commit(false);

            TableWorks tableWorks = new TableWorks(session, table);

            tableWorks.dropConstraint(name, cascade);

            return;
        }

        public void processAlterColumn(Table table, ColumnSchema column,
                                int columnIndex)
        {

            int position = getPosition();

            switch (token.tokenType)
            {

                case Tokens.RENAME:
                    {
                        read();
                        readThis(Tokens.TO);
                        processAlterColumnRename(table, column);

                        return;
                    }
                case Tokens.DROP:
                    {
                        read();

                        if (token.tokenType == Tokens.DEFAULT)
                        {
                            read();

                            TableWorks tw = new TableWorks(session, table);

                            tw.setColDefaultExpression(columnIndex, null);

                            return;
                        }
                        else if (token.tokenType == Tokens.GENERATED)
                        {
                            read();
                            column.setIdentity(null);
                            table.setColumnTypeVars(columnIndex);

                            return;
                        }
                        else
                        {
                            throw unexpectedToken();
                        }
                    }
                case Tokens.SET:
                    {
                        read();

                        switch (token.tokenType)
                        {

                            case Tokens.DATA:
                                {
                                    read();
                                    readThis(Tokens.TYPE);
                                    processAlterColumnDataType(table, column);

                                    return;
                                }
                            case Tokens.DEFAULT:
                                {
                                    read();

                                    //ALTER TABLE .. ALTER COLUMN .. SET DEFAULT
                                    TableWorks tw = new TableWorks(session, table);
                                    SqlType type = column.getDataType();
                                    Expression expr = this.readDefaultClause(type);

                                    tw.setColDefaultExpression(columnIndex, expr);

                                    return;
                                }
                            case Tokens.NOT:
                                {

                                    //ALTER TABLE .. ALTER COLUMN .. SET NOT NULL
                                    read();
                                    readThis(Tokens.NULL);
                                    session.commit(false);

                                    TableWorks tw = new TableWorks(session, table);

                                    tw.setColNullability(column, false);

                                    return;
                                }
                            case Tokens.NULL:
                                {
                                    read();

                                    //ALTER TABLE .. ALTER COLUMN .. SET NULL
                                    session.commit(false);

                                    TableWorks tw = new TableWorks(session, table);

                                    tw.setColNullability(column, true);

                                    return;
                                }
                            default:
                                rewind(position);
                                read();
                                break;
                        }
                    }
                    break;
               
            }

            if (token.tokenType == Tokens.SET
                    || token.tokenType == Tokens.RESTART)
            {
                if (!column.isIdentity())
                {
                    throw Error.error(ErrorCode.X_42535);
                }

                processAlterColumnSequenceOptions(column);

                return;
            }
            else
            {
                processAlterColumnType(table, column, true);

                return;
            }
        }

        public Statement compileAlterColumn(Table table, ColumnSchema column,
                                     int columnIndex)
        {

            int position = getPosition();

            switch (token.tokenType)
            {

                case Tokens.RENAME:
                    {
                        read();
                        readThis(Tokens.TO);

                        return compileAlterColumnRename(table, column);
                    }
                case Tokens.DROP:
                    {
                        read();

                        if (token.tokenType == Tokens.DEFAULT)
                        {
                            read();

                            return compileAlterColumnDropDefault(table, column,
                                                                 columnIndex);
                        }
                        else if (token.tokenType == Tokens.GENERATED)
                        {
                            read();

                            return compileAlterColumnDropGenerated(table, column,
                                                                   columnIndex);
                        }
                        else
                        {
                            throw unexpectedToken();
                        }
                    }
                case Tokens.SET:
                    {
                        read();

                        switch (token.tokenType)
                        {

                            case Tokens.DATA:
                                {
                                    read();
                                    readThis(Tokens.TYPE);

                                    return compileAlterColumnDataType(table, column);
                                }
                            case Tokens.DEFAULT:
                                {
                                    read();

                                    //ALTER TABLE .. ALTER COLUMN .. SET DEFAULT
                                    SqlType type = column.getDataType();
                                    Expression expr = this.readDefaultClause(type);

                                    return compileAlterColumnSetDefault(table, column,
                                                                        expr);
                                }
                            case Tokens.NOT:
                                {

                                    //ALTER TABLE .. ALTER COLUMN .. SET NOT NULL
                                    read();
                                    readThis(Tokens.NULL);

                                    return compileAlterColumnSetNullability(table, column,
                                            false);
                                }
                            case Tokens.NULL:
                                {
                                    read();

                                    return compileAlterColumnSetNullability(table, column,
                                            true);
                                }
                            default:
                                rewind(position);
                                read();
                                break;
                        }
                    }

                    goto default;
                // fall through
                default:
                    break;
            }

            if (token.tokenType == Tokens.SET
                    || token.tokenType == Tokens.RESTART)
            {
                if (!column.isIdentity())
                {
                    throw Error.error(ErrorCode.X_42535);
                }

                return compileAlterColumnSequenceOptions(table, column);
            }
            else
            {
                return compileAlterColumnType(table, column);
            }
        }

        private Statement compileAlterColumnDataType(Table table,
                ColumnSchema column)
        {

            //QNameManager.QName writeName = null;
            SqlType typeObject = readTypeDefinition(false);
            String sql = getLastPart();
            Object[] args = new Object[] {
            table, column, typeObject
        };

            return new StatementSchema(sql, StatementTypes.ALTER_TABLE, null,
                                       null, table.getName());
        }

        private Statement compileAlterColumnType(Table table,
                ColumnSchema column)
        {

            String sql = base.getStatement(getParsePosition(),
                                            endStatementTokens);

            return new StatementSchema(sql, StatementTypes.ALTER_TABLE, null,
                                       table.getName());
        }

        private Statement compileAlterColumnSequenceOptions(Table table,
                ColumnSchema column)
        {

            String sql = base.getStatement(getParsePosition(),
                                            endStatementTokens);

            return new StatementSchema(sql, StatementTypes.ALTER_TABLE,
                                       table.getName(), null);
        }

        private Statement compileAlterColumnSetNullability(Table table,
                ColumnSchema column, bool b)
        {

            String sql = base.getStatement(getParsePosition(),
                                            endStatementTokens);

            return new StatementSchema(sql, StatementTypes.ALTER_TABLE,
                                       table.getName(), null);
        }

        private Statement compileAlterColumnSetDefault(Table table,
                ColumnSchema column, Expression expr)
        {

            String sql = base.getStatement(getParsePosition(),
                                            endStatementTokens);

            return new StatementSchema(sql, StatementTypes.ALTER_TABLE,
                                       table.getName(), null);
        }

        private Statement compileAlterColumnDropGenerated(Table table,
                ColumnSchema column, int columnIndex)
        {

            String sql = base.getStatement(getParsePosition(),
                                            endStatementTokens);

            return new StatementSchema(sql, StatementTypes.ALTER_TABLE,
                                       table.getName(), null);
        }

        private Statement compileAlterColumnDropDefault(Table table,
                ColumnSchema column, int columnIndex)
        {

            String sql = base.getStatement(getParsePosition(),
                                            endStatementTokens);

            return new StatementSchema(sql, StatementTypes.ALTER_TABLE,
                                       table.getName(), null);
        }

        public Statement compileAlterSequence()
        {

            QNameManager.QName schema = session.getSchemaQName(token.namePrefix);
            NumberSequence sequence =
                database.schemaManager.getSequence(token.tokenString, schema.name,
                                                   true);

            read();

            if (token.tokenType == Tokens.RENAME)
            {
                read();
                readThis(Tokens.TO);

                return compileRenameObject(sequence.getName(),
                                           SchemaObjectTypes.SEQUENCE);
            }

            NumberSequence copy = sequence.duplicate();

            readSequenceOptions(copy, false, true);

            String sql = getLastPart();
            Object[] args = new Object[] {
            sequence, copy
        };

            return new StatementSchema(sql, StatementTypes.ALTER_SEQUENCE, args,
                                       null, null);
        }

        public void processAlterColumnSequenceOptions(ColumnSchema column)
        {

            OrderedIntHashSet set = new OrderedIntHashSet();
            NumberSequence sequence = column.getIdentitySequence().duplicate();

            while (true)
            {
                bool end = false;

                switch (token.tokenType)
                {

                    case Tokens.RESTART:
                        {
                            if (!set.add(token.tokenType))
                            {
                                throw unexpectedToken();
                            }

                            read();
                            readThis(Tokens.WITH);

                            long value = readBigint();

                            sequence.setStartValue(value);

                            break;
                        }
                    case Tokens.SET:
                        read();

                        switch (token.tokenType)
                        {

                            case Tokens.INCREMENT:
                                {
                                    if (!set.add(token.tokenType))
                                    {
                                        throw unexpectedToken();
                                    }

                                    read();
                                    readThis(Tokens.BY);

                                    long value = readBigint();

                                    sequence.setIncrement(value);

                                    break;
                                }
                            case Tokens.NO:
                                read();

                                if (token.tokenType == Tokens.MAXVALUE)
                                {
                                    sequence.setDefaultMaxValue();
                                }
                                else if (token.tokenType == Tokens.MINVALUE)
                                {
                                    sequence.setDefaultMinValue();
                                }
                                else if (token.tokenType == Tokens.CYCLE)
                                {
                                    sequence.setCycle(false);
                                }
                                else
                                {
                                    throw unexpectedToken();
                                }

                                if (!set.add(token.tokenType))
                                {
                                    throw unexpectedToken();
                                }

                                read();
                                break;

                            case Tokens.MAXVALUE:
                                {
                                    if (!set.add(token.tokenType))
                                    {
                                        throw unexpectedToken();
                                    }

                                    read();

                                    long value = readBigint();

                                    sequence.setMaxValueNoCheck(value);

                                    break;
                                }
                            case Tokens.MINVALUE:
                                {
                                    if (!set.add(token.tokenType))
                                    {
                                        throw unexpectedToken();
                                    }

                                    read();

                                    long value = readBigint();

                                    sequence.setMinValueNoCheck(value);

                                    break;
                                }
                            case Tokens.CYCLE:
                                if (!set.add(token.tokenType))
                                {
                                    throw unexpectedToken();
                                }

                                read();
                                sequence.setCycle(true);
                                break;

                            default:
                                throw Error.error(ErrorCode.X_42581,
                                                  token.tokenString);
                        }
                        break;

                    default:
                        end = true;
                        break;
                }

                if (end)
                {
                    break;
                }
            }

            sequence.checkValues();
            column.getIdentitySequence().reset(sequence);
        }

        /**
         * Should allow only limited changes to column type
         */
        private void processAlterColumnDataType(Table table, ColumnSchema oldCol)
        {
            processAlterColumnType(table, oldCol, false);
        }

        /**
         * Allows changes to type of column or addition of an IDENTITY generator.
         * IDENTITY is not removed if it does not appear in new column definition
         * Constraint definitions are not allowed
         */
        private void processAlterColumnType(Table table, ColumnSchema oldCol,
                                            bool fullDefinition)
        {

            ColumnSchema newCol;

            if (oldCol.isGenerated())
            {
                throw Error.error(ErrorCode.X_42561);
            }

            if (fullDefinition)
            {
                EfzArrayList list = new EfzArrayList();
                Constraint c = table.getPrimaryConstraint();

                if (c == null)
                {
                    c = new Constraint(null, null,
                                       SchemaObjectConstraintTypes.TEMP);
                }

                list.add(c);

                newCol = readColumnDefinitionOrNull(table, oldCol.getName(), list);

                if (newCol == null)
                {
                    throw Error.error(ErrorCode.X_42000);
                }

                if (oldCol.isIdentity() && newCol.isIdentity())
                {
                    throw Error.error(ErrorCode.X_42525);
                }

                if (list.size() > 1)
                {
                    throw Error.error(ErrorCode.X_42524);
                }
            }
            else
            {
                SqlType type = readTypeDefinition(true);

                if (oldCol.isIdentity())
                {
                    if (!type.isIntegralType())
                    {
                        throw Error.error(ErrorCode.X_42561);
                    }
                }

                newCol = oldCol.duplicate();

                newCol.setType(type);
            }

            TableWorks tw = new TableWorks(session, table);

            tw.retypeColumn(oldCol, newCol);
        }

        /**
         * Responsible for handling tail of ALTER COLUMN ... RENAME ...
         */
        private void processAlterColumnRename(Table table, ColumnSchema column)
        {

            checkIsSimpleName();

            if (table.findColumn(token.tokenString) > -1)
            {
                throw Error.error(ErrorCode.X_42504, token.tokenString);
            }

            database.schemaManager.checkColumnIsReferenced(table.getName(),
                    column.getName());
            session.commit(false);
            table.renameColumn(column, token.tokenString, isDelimitedIdentifier());
            read();
        }

        private Statement compileAlterColumnRename(Table table,
                ColumnSchema column)
        {

            checkIsSimpleName();

            QNameManager.QName name = readNewSchemaObjectName(SchemaObjectTypes.COLUMN, true);

            if (table.findColumn(name.name) > -1)
            {
                throw Error.error(ErrorCode.X_42504, name.name);
            }

            database.schemaManager.checkColumnIsReferenced(table.getName(),
                    column.getName());

            String sql = getLastPart();
            Object[] args = new Object[] {
            column.getName(), name
        };

            return new StatementSchema(sql, StatementTypes.RENAME_OBJECT, args,
                                       null, null);
        }

        public Statement compileAlterSchemaRename()
        {

            QNameManager.QName name = readSchemaName();

            checkSchemaUpdateAuthorisation(name);
            readThis(Tokens.RENAME);
            readThis(Tokens.TO);

            QNameManager.QName newName = readNewSchemaName();
            String sql = getLastPart();
            Object[] args = new Object[] {
            name, newName
        };

            return new StatementSchema(sql, StatementTypes.RENAME_OBJECT, args,
                                       null, null);
        }

        public Statement compileAlterUser()
        {

            String password;
            User userObject;
            QNameManager.QName userName = readNewUserIdentifier();

            userObject = database.getUserManager().get(userName.name);

            if (userName.name.Equals(Tokens.T_PUBLIC))
            {
                throw Error.error(ErrorCode.X_42503);
            }

            readThis(Tokens.SET);

            if (token.tokenType == Tokens.PASSWORD)
            {
                read();

                password = readPassword();

                Object[] args = new Object[] {
                userObject, password
            };

                return new StatementCommand(StatementTypes.SET_USER_PASSWORD,
                                            args, null, null);
            }
            else if (token.tokenType == Tokens.INITIAL)
            {
                read();
                readThis(Tokens.SCHEMA);

                QNameManager.QName schemaName;

                if (token.tokenType == Tokens.DEFAULT)
                {
                    schemaName = null;
                }
                else
                {
                    schemaName = database.schemaManager.getSchemaHsqlName(
                        token.tokenString);
                }

                read();

                Object[] args = new Object[] {
                userObject, schemaName
            };

                return new StatementCommand(StatementTypes.SET_USER_INITIAL_SCHEMA,
                                            args, null, null);
            }
            else
            {
                throw unexpectedToken();
            }
        }

        public void processAlterDomain()
        {

            QNameManager.QName schema = session.getSchemaQName(token.namePrefix);

            checkSchemaUpdateAuthorisation(schema);

            SqlType domain = database.schemaManager.getDomain(token.tokenString,
                schema.name, true);

            read();

            switch (token.tokenType)
            {

                case Tokens.RENAME:
                    {
                        read();
                        readThis(Tokens.TO);

                        QNameManager.QName newName = readNewSchemaObjectName(SchemaObjectTypes.DOMAIN,
                            true);

                        newName.setSchemaIfNull(schema);

                        if (domain.getSchemaName() != newName.schema)
                        {
                            throw Error.error(ErrorCode.X_42505, newName.schema.name);
                        }

                        checkSchemaUpdateAuthorisation(schema);
                        database.schemaManager.renameSchemaObject(domain.getName(),
                                newName);

                        return;
                    }
                case Tokens.DROP:
                    {
                        read();

                        if (token.tokenType == Tokens.DEFAULT)
                        {
                            read();
                            domain.userTypeModifier.removeDefaultClause();

                            return;
                        }
                        else if (token.tokenType == Tokens.CONSTRAINT)
                        {
                            read();
                            checkIsSchemaObjectName();

                            QNameManager.QName name = database.schemaManager.getSchemaObjectName(
                                domain.getSchemaName(), token.tokenString,
                                SchemaObjectTypes.CONSTRAINT, true);

                            read();
                            database.schemaManager.removeSchemaObject(name);

                            return;
                        }
                        else
                        {
                            throw unexpectedToken();
                        }
                    }
                case Tokens.SET:
                    {
                        read();
                        readThis(Tokens.DEFAULT);

                        Expression e = readDefaultClause(domain);

                        domain.userTypeModifier.setDefaultClause(e);

                        return;
                    }
                case Tokens.ADD:
                    {
                        read();

                        if (token.tokenType == Tokens.CONSTRAINT
                                || token.tokenType == Tokens.CHECK)
                        {
                            EfzArrayList tempConstraints = new EfzArrayList();

                            readConstraint(domain, tempConstraints);

                            Constraint c = (Constraint)tempConstraints.get(0);

                            domain.userTypeModifier.addConstraint(c);
                            database.schemaManager.addSchemaObject(c);

                            return;
                        }
                    }
                    break;
            }

            throw unexpectedToken();
        }

        public Statement compileAlterDomain()
        {

            QNameManager.QName schema = session.getSchemaQName(token.namePrefix);
            SqlType domain = database.schemaManager.getDomain(token.tokenString,
                schema.name, true);

            read();

            switch (token.tokenType)
            {

                case Tokens.RENAME:
                    {
                        read();
                        readThis(Tokens.TO);

                        return compileRenameObject(domain.getName(),
                                                   SchemaObjectTypes.DOMAIN);
                    }
                case Tokens.DROP:
                    {
                        read();

                        if (token.tokenType == Tokens.DEFAULT)
                        {
                            read();

                            return compileAlterDomainDropDefault(domain);
                        }
                        else if (token.tokenType == Tokens.CONSTRAINT)
                        {
                            read();
                            checkIsSchemaObjectName();

                            QNameManager.QName name = database.schemaManager.getSchemaObjectName(
                                domain.getSchemaName(), token.tokenString,
                                SchemaObjectTypes.CONSTRAINT, true);

                            read();

                            return compileAlterDomainDropConstraint(domain, name);
                        }
                        else
                        {
                            throw unexpectedToken();
                        }
                    }
                case Tokens.SET:
                    {
                        read();
                        readThis(Tokens.DEFAULT);

                        Expression e = readDefaultClause(domain);

                        return compileAlterDomainSetDefault(domain, e);
                    }
                case Tokens.ADD:
                    {
                        read();

                        if (token.tokenType == Tokens.CONSTRAINT
                                || token.tokenType == Tokens.CHECK)
                        {
                            EfzArrayList tempConstraints = new EfzArrayList();

                            readConstraint(domain, tempConstraints);

                            Constraint c = (Constraint)tempConstraints.get(0);

                            return compileAlterDomainAddConstraint(domain, c);
                        }
                    }
                    break;
            }

            throw unexpectedToken();
        }

        private Statement compileAlterDomainAddConstraint(SqlType domain,
                Constraint c)
        {

            String sql = base.getStatement(getParsePosition(),
                                            endStatementTokens);

            return new StatementSchema(sql, StatementTypes.ALTER_DOMAIN, null,
                                       null);
        }

        private Statement compileAlterDomainSetDefault(SqlType domain, Expression e)
        {

            String sql = base.getStatement(getParsePosition(),
                                            endStatementTokens);

            return new StatementSchema(sql, StatementTypes.ALTER_DOMAIN, null,
                                       null);
        }

        private Statement compileAlterDomainDropConstraint(SqlType domain,
                QNameManager.QName name)
        {

            String sql = base.getStatement(getParsePosition(),
                                            endStatementTokens);

            return new StatementSchema(sql, StatementTypes.ALTER_DOMAIN, null,
                                       null);
        }

        private Statement compileAlterDomainDropDefault(SqlType domain)
        {

            String sql = getStatement(getParsePosition(), endStatementTokens);

            return new StatementSchema(sql, StatementTypes.ALTER_DOMAIN, null,
                                       null);
        }

        private bool isGrantToken()
        {

            switch (token.tokenType)
            {

                case Tokens.ALL:
                case Tokens.INSERT:
                case Tokens.UPDATE:
                case Tokens.SELECT:
                case Tokens.DELETE:
                case Tokens.USAGE:
                case Tokens.EXECUTE:
                case Tokens.REFERENCES:
                    return true;

                default:
                    return false;
            }
        }

        public StatementSchema compileGrantOrRevoke()
        {

            bool grant = token.tokenType == Tokens.GRANT;

            read();

            if (isGrantToken()
                    || (!grant
                        && (token.tokenType == Tokens.GRANT
                            || token.tokenType == Tokens.HIERARCHY)))
            {
                return compileRightGrantOrRevoke(grant);
            }
            else
            {
                return compileRoleGrantOrRevoke(grant);
            }
        }

        private StatementSchema compileRightGrantOrRevoke(bool grant)
        {

            OrderedHashSet granteeList = new OrderedHashSet();
            Grantee grantor = null;
            Right right = null;

            //        SchemaObject   schemaObject;
            QNameManager.QName objectName = null;
            bool isTable = false;
            bool isUsage = false;
            bool isExec = false;
            bool isAll = false;
            bool isGrantOption = false;
            bool cascade = false;

            if (!grant)
            {
                if (token.tokenType == Tokens.GRANT)
                {
                    read();
                    readThis(Tokens.OPTION);
                    readThis(Tokens.FOR);

                    isGrantOption = true;

                    // throw not suppoerted
                }
                else if (token.tokenType == Tokens.HIERARCHY)
                {
                    throw unsupportedFeature();
                    /*
                                    read();
                                    readThis(Token.OPTION);
                                    readThis(Token.FOR);
                    */
                }
            }

            // ALL means all the rights the grantor can grant
            if (token.tokenType == Tokens.ALL)
            {
                read();

                if (token.tokenType == Tokens.PRIVILEGES)
                {
                    read();
                }

                right = Right.fullRights;
                isAll = true;
            }
            else
            {
                right = new Right();

                bool loop = true;

                while (loop)
                {
                    checkIsNotQuoted();

                    int rightType =
                        GranteeManager.getCheckSingleRight(token.tokenString);
                    int grantType = token.tokenType;
                    OrderedHashSet columnSet = null;

                    read();

                    switch (grantType)
                    {

                        case Tokens.REFERENCES:
                        case Tokens.SELECT:
                        case Tokens.INSERT:
                        case Tokens.UPDATE:
                            if (token.tokenType == Tokens.OPENBRACKET)
                            {
                                columnSet = readColumnNames(false);
                            }
                            goto case Tokens.DELETE;
                        // fall through
                        case Tokens.DELETE:
                        case Tokens.TRIGGER:
                            if (right == null)
                            {
                                right = new Right();
                            }

                            right.set(rightType, columnSet);

                            isTable = true;
                            break;

                        case Tokens.USAGE:
                            if (isTable)
                            {
                                throw unexpectedToken();
                            }

                            right = Right.fullRights;
                            isUsage = true;
                            loop = false;

                            continue;
                        case Tokens.EXECUTE:
                            if (isTable)
                            {
                                throw unexpectedToken();
                            }

                            right = Right.fullRights;
                            isExec = true;
                            loop = false;

                            continue;
                    }

                    if (token.tokenType == Tokens.COMMA)
                    {
                        read();

                        continue;
                    }

                    break;
                }
            }

            readThis(Tokens.ON);

            int objectType = 0;

            switch (token.tokenType)
            {

                case Tokens.CLASS:
                    if (!isExec && !isAll)
                    {
                        throw unexpectedToken();
                    }

                    read();

                    if (!isSimpleName() || !isDelimitedIdentifier())
                    {
                        throw Error.error(ErrorCode.X_42569);
                    }

                    objectType = SchemaObjectTypes.FUNCTION;
                    objectName = readNewSchemaObjectName(SchemaObjectTypes.FUNCTION,
                                                         false);
                    break;

                case Tokens.SPECIFIC:
                    {
                        if (!isExec && !isAll)
                        {
                            throw unexpectedToken();
                        }

                        read();

                        switch (token.tokenType)
                        {

                            case Tokens.ROUTINE:
                            case Tokens.PROCEDURE:
                            case Tokens.FUNCTION:
                                read();
                                break;

                            default:
                                throw unexpectedToken();
                        }

                        objectType = SchemaObjectTypes.SPECIFIC_ROUTINE;

                        break;
                    }
                case Tokens.FUNCTION:
                    if (!isExec && !isAll)
                    {
                        throw unexpectedToken();
                    }

                    read();

                    objectType = SchemaObjectTypes.FUNCTION;
                    break;

                case Tokens.PROCEDURE:
                    if (!isExec && !isAll)
                    {
                        throw unexpectedToken();
                    }

                    read();

                    objectType = SchemaObjectTypes.PROCEDURE;
                    break;

                case Tokens.ROUTINE:
                    if (!isExec && !isAll)
                    {
                        throw unexpectedToken();
                    }

                    read();

                    objectType = SchemaObjectTypes.ROUTINE;
                    break;

                case Tokens.TYPE:
                    if (!isUsage && !isAll)
                    {
                        throw unexpectedToken();
                    }

                    read();

                    objectType = SchemaObjectTypes.TYPE;
                    break;

                case Tokens.DOMAIN:
                    if (!isUsage && !isAll)
                    {
                        throw unexpectedToken();
                    }

                    read();

                    objectType = SchemaObjectTypes.DOMAIN;
                    break;

                case Tokens.SEQUENCE:
                    if (!isUsage && !isAll)
                    {
                        throw unexpectedToken();
                    }

                    read();

                    objectType = SchemaObjectTypes.SEQUENCE;
                    break;

                case Tokens.CHARACTER:
                    if (!isUsage && !isAll)
                    {
                        throw unexpectedToken();
                    }

                    read();
                    readThis(Tokens.SET);

                    objectType = SchemaObjectTypes.CHARSET;
                    break;

                case Tokens.TABLE:
                default:
                    if (!isTable && !isAll)
                    {
                        throw unexpectedToken();
                    }

                    readIfThis(Tokens.TABLE);

                    objectType = SchemaObjectTypes.TABLE;
                    break;
            }

            objectName = readNewSchemaObjectName(objectType, false);

            if (grant)
            {
                readThis(Tokens.TO);
            }
            else
            {
                readThis(Tokens.FROM);
            }

            while (true)
            {
                checkIsSimpleName();
                granteeList.add(token.tokenString);
                read();

                if (token.tokenType == Tokens.COMMA)
                {
                    read();
                }
                else
                {
                    break;
                }
            }

            if (grant)
            {
                if (token.tokenType == Tokens.WITH)
                {
                    read();
                    readThis(Tokens.GRANT);
                    readThis(Tokens.OPTION);

                    isGrantOption = true;
                }

                /** @todo - implement */
                if (token.tokenType == Tokens.GRANTED)
                {
                    read();
                    readThis(Tokens.BY);

                    if (token.tokenType == Tokens.CURRENT_USER)
                    {
                        read();

                        //
                    }
                    else
                    {
                        readThis(Tokens.CURRENT_ROLE);

                        if (session.getRole() == null)
                        {
                            throw Error.error(ErrorCode.X_0P000);
                        }

                        grantor = session.getRole();
                    }
                }
            }
            else
            {
                if (token.tokenType == Tokens.CASCADE)
                {
                    cascade = true;

                    read();
                }
                else
                {
                    readThis(Tokens.RESTRICT);
                }
            }

            int typee = grant ? StatementTypes.GRANT
                                   : StatementTypes.REVOKE;
            Object[] args = new Object[] {
            granteeList, objectName, right, grantor,  (cascade),
             (isGrantOption)
        };
            String sql = getLastPart();
            StatementSchema cs = new StatementSchema(sql, typee, args, null, null);

            return cs;
        }

        private StatementSchema compileRoleGrantOrRevoke(bool grant)
        {

            Grantee grantor = session.getGrantee();
            OrderedHashSet roleList = new OrderedHashSet();
            OrderedHashSet granteeList = new OrderedHashSet();
            bool cascade = false;

            if (!grant && token.tokenType == Tokens.ADMIN)
            {
                throw unsupportedFeature();
                /*
                            read();
                            readThis(Token.OPTION);
                            readThis(Token.FOR);
                */
            }

            while (true)
            {
                checkIsSimpleName();
                roleList.add(token.tokenString);
                read();

                if (token.tokenType == Tokens.COMMA)
                {
                    read();

                    continue;
                }

                break;
            }

            if (grant)
            {
                readThis(Tokens.TO);
            }
            else
            {
                readThis(Tokens.FROM);
            }

            while (true)
            {
                checkIsSimpleName();
                granteeList.add(token.tokenString);
                read();

                if (token.tokenType == Tokens.COMMA)
                {
                    read();
                }
                else
                {
                    break;
                }
            }

            if (grant)
            {
                if (token.tokenType == Tokens.WITH)
                {
                    throw unsupportedFeature();
                    /*
                                    read();
                                    readThis(Token.ADMIN);
                                    readThis(Token.OPTION);
                    */
                }
            }

            if (token.tokenType == Tokens.GRANTED)
            {
                read();
                readThis(Tokens.BY);

                if (token.tokenType == Tokens.CURRENT_USER)
                {
                    read();

                    //
                }
                else
                {
                    readThis(Tokens.CURRENT_ROLE);

                    if (session.getRole() == null)
                    {
                        throw Error.error(ErrorCode.X_0P000);
                    }

                    grantor = session.getRole();
                }
            }

            if (!grant)
            {
                if (token.tokenType == Tokens.CASCADE)
                {
                    cascade = true;

                    read();
                }
                else
                {
                    readThis(Tokens.RESTRICT);
                }
            }

            int type = grant ? StatementTypes.GRANT_ROLE
                                         : StatementTypes.REVOKE_ROLE;
            Object[] args = new Object[] {
            granteeList, roleList, grantor,  (cascade)
        };
            String sql = getLastPart();
            StatementSchema cs = new StatementSchema(sql, type, args, null, null);

            return cs;
        }

        public void checkSchemaUpdateAuthorisation(QNameManager.QName schema)
        {

            if (session.isProcessingLog())
            {
                return;
            }

            SqlInvariants.checkSchemaNameNotSystem(schema.name);

            if (isSchemaDefinition)
            {
                if (schema != session.getCurrentSchemaHsqlName())
                {
                    throw Error.error(ErrorCode.X_42505);
                }
            }
            else
            {
                session.getGrantee().checkSchemaUpdateOrGrantRights(schema.name);
            }

            session.checkDDLWrite();
        }

        public void checkDatabaseUpdateAuthorisation()
        {
            session.checkAdmin();
            session.checkDDLWrite();
        }
    }
}
